blob: 559481e7a9ec63f838c1c68b87df6d9fe31d0c7b [file] [log] [blame]
Philipp Schradere2e27ff2024-02-25 22:08:55 -08001/// <reference types="cypress" />
2
3// On the 3rd row of matches (index 2) click on the third team
4// (index 2) which resolves to team 333 in quals match 3.
5const QUALS_MATCH_3_TEAM_333 = 2 * 6 + 2;
6
7function disableAlerts() {
8 cy.get('#block_alerts').check({force: true}).should('be.checked');
9}
10
11function switchToTab(tabName) {
12 cy.contains('.nav-link', tabName).click();
13}
14
15function headerShouldBe(text) {
16 cy.get('.header').should('have.text', text);
17}
18
19function clickButton(buttonName) {
20 cy.contains('button', buttonName).click();
21}
22
23// Wrapper around cy.exec() because it truncates the output of the subprocess
24// if it fails. This is a work around to manually print the full error on the
25// console if a failure happends.
26function exec(command) {
27 cy.exec(command, {failOnNonZeroExit: false}).then((result) => {
28 if (result.code) {
29 throw new Error(`Execution of "${command}" failed
30 Exit code: ${result.code}
31 Stdout:\n${result.stdout}
32 Stderr:\n${result.stderr}`);
33 }
34 });
35}
36
37// Prepares data entry so that we _could_ hit Submit.
38//
39// Options:
40// matchButtonKey: The index into the big matchlist table that we want to
41// click on to start the data entry.
42// teamNumber: The team number that matches the button that we click on as
43// specified by `matchButtonKey`.
44//
45// TODO(phil): Deduplicate with scouting_test.cy.js.
46function prepareDataScouting(options) {
47 const {matchButtonKey = SEMI_FINAL_2_MATCH_3_TEAM_5254, teamNumber = 5254} =
48 options;
49
50 // Click on a random team in the Match list. The exact details here are not
51 // important, but we need to know what they are. This could as well be any
52 // other team from any other match.
53 cy.get('button.match-item').eq(matchButtonKey).click();
54
55 // Select Starting Position.
56 headerShouldBe(teamNumber + ' Init ');
57 cy.get('[type="radio"]').first().check();
58 clickButton('Start Match');
59
60 // Pick and Place Note in Auto.
61 clickButton('NOTE');
62 clickButton('AMP');
63
64 // Pick and Place Cube in Teleop.
65 clickButton('Start Teleop');
66 clickButton('NOTE');
67 clickButton('AMP AMPLIFIED');
68
69 // Generate some extra actions so that we are guaranteed to have at least 2
70 // QR codes.
71 for (let i = 0; i < 5; i++) {
72 clickButton('NOTE');
73 clickButton('AMP');
74 }
75
76 // Robot dead and revive.
77 clickButton('DEAD');
78 clickButton('Revive');
79
80 // Endgame.
81 clickButton('Endgame');
82 cy.contains(/Harmony/).click();
83
84 clickButton('End Match');
85 headerShouldBe(teamNumber + ' Review and Submit ');
86 cy.get('#review_data li')
87 .eq(0)
88 .should('have.text', ' Started match at position 1 ');
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070089 cy.get('#review_data li').eq(1).should('have.text', ' Picked up Note ');
Philipp Schradere2e27ff2024-02-25 22:08:55 -080090 cy.get('#review_data li')
91 .last()
92 .should(
93 'have.text',
94 ' Ended Match; stageType: kHARMONY, trapNote: false, spotlight: false '
95 );
96}
97
98before(() => {
99 cy.visit('/');
100 disableAlerts();
101 cy.title().should('eq', 'FRC971 Scouting Application');
102});
103
104beforeEach(() => {
105 cy.visit('/');
106 disableAlerts();
107});
108
109describe('Scouting app tests', () => {
110 // This test collects some scouting data and then generates the corresponding
111 // QR codes. The test takes screenshots of those QR codes. The QR codes get
112 // turned into a little video file for the browser to use as a fake camera
113 // input. The test then switches to the Scan tab to scan the QR codes from
114 // the "camera". We then make sure that the data gets submitted.
115 it('should: be able to generate and scan QR codes.', () => {
116 prepareDataScouting({
117 matchButtonKey: QUALS_MATCH_3_TEAM_333,
118 teamNumber: 333,
119 });
120 clickButton('Create QR Code');
121 headerShouldBe('333 QR Code ');
122
123 cy.get('#qr_code_piece_size').select('150');
124
125 // Go into a mobile-phone view so that we can guarantee that the QR code is
126 // visible.
127 cy.viewport(400, 660);
128
129 cy.get('.qrcode-buttons > li > a')
130 .should('have.length.at.least', 4)
131 .each(($button, index, $buttons) => {
132 if (index == 0 || index + 1 == $buttons.length) {
133 // Skip the "Previous" and "Next" buttons.
134 return;
135 }
136 // Click on the button to switch to that particular QR code.
137 // We use force:true here because without bootstrap (inside the
138 // sandbox) the buttons overlap one another a bit.
139 cy.wrap($button).click({force: true});
140 cy.get('div.qrcode').screenshot(`qrcode_${index}_screenshot`);
141 });
142
143 exec('./testing/camera_simulator/camera_simulator_/camera_simulator');
144
145 switchToTab('Scan');
146
147 // Since we cannot reliably predict how long it will take to scan all the
148 // QR codes, we use a really long timeout here.
149 cy.get('.progress_message', {timeout: 80000}).should('contain', 'Success!');
150
151 // Now that the data is submitted, the button should be disabled.
152 switchToTab('Match List');
153 cy.get('button.match-item')
154 .eq(QUALS_MATCH_3_TEAM_333)
155 .should('be.disabled');
156 });
157});