Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 1 | /// <reference types="cypress" /> |
| 2 | |
Philipp Schrader | e11114f | 2023-04-15 17:04:25 -0700 | [diff] [blame] | 3 | // On the 87th row of matches (index 86) click on the second team |
| 4 | // (index 1) which resolves to team 5254 in semi final 2 match 3. |
| 5 | const SEMI_FINAL_2_MATCH_3_TEAM_5254 = 86 * 6 + 1; |
| 6 | |
| 7 | // On the 1st row of matches (index 0) click on the fourth team |
| 8 | // (index 3) which resolves to team 3990 in quals match 1. |
| 9 | const QUALS_MATCH_1_TEAM_3990 = 0 * 6 + 3; |
| 10 | |
Filip Kujawa | c1ded37 | 2023-05-27 14:33:43 -0700 | [diff] [blame^] | 11 | // On the 2st row of matches (index 1) click on the fourth team |
| 12 | // (index 3) which resolves to team 4481 in quals match 1. |
| 13 | const QUALS_MATCH_2_TEAM_4481 = 1 * 6 + 3; |
| 14 | |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 15 | function disableAlerts() { |
| 16 | cy.get('#block_alerts').check({force: true}).should('be.checked'); |
| 17 | } |
| 18 | |
| 19 | function switchToTab(tabName) { |
| 20 | cy.contains('.nav-link', tabName).click(); |
| 21 | } |
| 22 | |
| 23 | function headerShouldBe(text) { |
| 24 | cy.get('.header').should('have.text', text); |
| 25 | } |
| 26 | |
| 27 | function clickButton(buttonName) { |
| 28 | cy.contains('button', buttonName).click(); |
| 29 | } |
| 30 | |
| 31 | function setInputTo(fieldSelector, value) { |
| 32 | cy.get(fieldSelector).type('{selectAll}' + value); |
| 33 | } |
| 34 | |
| 35 | // Moves the nth slider left or right. A positive "adjustBy" value moves the |
| 36 | // slider to the right. A negative value moves the slider to the left. |
| 37 | // |
| 38 | // negative/left <--- 0 ---> positive/right |
| 39 | function adjustNthSliderBy(n, adjustBy) { |
| 40 | let element = cy.get('input[type=range]').eq(n); |
| 41 | element.scrollIntoView(); |
| 42 | element.invoke('val').then((currentValue) => { |
| 43 | // We need to query for the slider here again because `invoke('val')` above |
| 44 | // somehow invalidates further calls to `val`. |
| 45 | cy.get('input[type=range]') |
| 46 | .eq(n) |
| 47 | .invoke('val', currentValue + adjustBy) |
| 48 | .trigger('change'); |
| 49 | }); |
| 50 | } |
| 51 | |
| 52 | // Asserts that the field on the "Submit and Review" screen has a specific |
| 53 | // value. |
| 54 | function expectReviewFieldToBe(fieldName, expectedValue) { |
| 55 | expectNthReviewFieldToBe(fieldName, 0, expectedValue); |
| 56 | } |
| 57 | |
| 58 | // Asserts that the n'th instance of a field on the "Submit and Review" |
| 59 | // screen has a specific value. |
| 60 | function expectNthReviewFieldToBe(fieldName, n, expectedValue) { |
| 61 | getNthReviewField(fieldName, n).should( |
| 62 | 'have.text', |
| 63 | `${fieldName}: ${expectedValue}` |
| 64 | ); |
| 65 | } |
| 66 | |
| 67 | function getNthReviewField(fieldName, n) { |
| 68 | let element = cy.get('li').filter(`:contains("${fieldName}: ")`).eq(n); |
| 69 | element.scrollIntoView(); |
| 70 | return element; |
| 71 | } |
| 72 | |
Filip Kujawa | c1ded37 | 2023-05-27 14:33:43 -0700 | [diff] [blame^] | 73 | function submitDataScouting( |
| 74 | matchButtonKey = SEMI_FINAL_2_MATCH_3_TEAM_5254, |
| 75 | teamNumber = 5254 |
| 76 | ) { |
| 77 | // Click on a random team in the Match list. The exact details here are not |
| 78 | // important, but we need to know what they are. This could as well be any |
| 79 | // other team from any other match. |
| 80 | cy.get('button.match-item').eq(matchButtonKey).click(); |
| 81 | |
| 82 | // Select Starting Position. |
| 83 | headerShouldBe(teamNumber + ' Init '); |
| 84 | cy.get('[type="radio"]').first().check(); |
| 85 | clickButton('Start Match'); |
| 86 | |
| 87 | // Pick and Place Cone in Auto. |
| 88 | clickButton('CONE'); |
| 89 | clickButton('HIGH'); |
| 90 | |
| 91 | // Pick and Place Cube in Teleop. |
| 92 | clickButton('Start Teleop'); |
| 93 | clickButton('CUBE'); |
| 94 | clickButton('LOW'); |
| 95 | |
| 96 | // Robot dead and revive. |
| 97 | clickButton('DEAD'); |
| 98 | clickButton('Revive'); |
| 99 | |
| 100 | // Endgame. |
| 101 | clickButton('Endgame'); |
| 102 | cy.contains(/Docked & Engaged/).click(); |
| 103 | |
| 104 | clickButton('End Match'); |
| 105 | headerShouldBe(teamNumber + ' Review and Submit '); |
| 106 | cy.get('#review_data li') |
| 107 | .eq(0) |
| 108 | .should('have.text', ' Started match at position 1 '); |
| 109 | cy.get('#review_data li').eq(1).should('have.text', ' Picked up kCone '); |
| 110 | cy.get('#review_data li') |
| 111 | .last() |
| 112 | .should( |
| 113 | 'have.text', |
| 114 | ' Ended Match; docked: false, engaged: true, attempted to dock and engage: false ' |
| 115 | ); |
| 116 | |
| 117 | clickButton('Submit'); |
| 118 | headerShouldBe(teamNumber + ' Success '); |
| 119 | } |
| 120 | |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 121 | before(() => { |
| 122 | cy.visit('/'); |
| 123 | disableAlerts(); |
| 124 | cy.title().should('eq', 'FRC971 Scouting Application'); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 125 | }); |
| 126 | |
| 127 | beforeEach(() => { |
| 128 | cy.visit('/'); |
| 129 | disableAlerts(); |
| 130 | }); |
| 131 | |
| 132 | describe('Scouting app tests', () => { |
| 133 | it('should: show matches in chronological order.', () => { |
| 134 | headerShouldBe('Matches'); |
| 135 | cy.get('.badge').eq(0).contains('Quals Match 1'); |
| 136 | cy.get('.badge').eq(1).contains('Quals Match 2'); |
| 137 | cy.get('.badge').eq(2).contains('Quals Match 3'); |
| 138 | cy.get('.badge').eq(9).contains('Quals Match 10'); |
| 139 | cy.get('.badge').eq(72).contains('Quarter Final 1 Match 1'); |
| 140 | cy.get('.badge').eq(73).contains('Quarter Final 2 Match 1'); |
| 141 | cy.get('.badge').eq(74).contains('Quarter Final 3 Match 1'); |
| 142 | cy.get('.badge').eq(75).contains('Quarter Final 4 Match 1'); |
| 143 | cy.get('.badge').eq(76).contains('Quarter Final 1 Match 2'); |
| 144 | cy.get('.badge').eq(82).contains('Semi Final 1 Match 1'); |
| 145 | cy.get('.badge').eq(83).contains('Semi Final 2 Match 1'); |
| 146 | cy.get('.badge').eq(84).contains('Semi Final 1 Match 2'); |
| 147 | cy.get('.badge').eq(85).contains('Semi Final 2 Match 2'); |
| 148 | cy.get('.badge').eq(89).contains('Final 1 Match 3'); |
| 149 | }); |
| 150 | |
Philipp Schrader | 8702b78 | 2023-04-15 17:33:37 -0700 | [diff] [blame] | 151 | it('should: prevent users from enter invalid match information.', () => { |
| 152 | switchToTab('Entry'); |
| 153 | headerShouldBe(' Team Selection '); |
| 154 | |
| 155 | setInputTo('#match_number', '1'); |
| 156 | setInputTo('#team_number', '5254'); |
| 157 | setInputTo('#set_number', '1'); |
| 158 | cy.get('#comp_level').select('Qualifications'); |
| 159 | |
| 160 | cy.contains('button', 'Next').should('be.disabled'); |
| 161 | }); |
| 162 | |
Philipp Schrader | e149885 | 2023-04-15 18:06:45 -0700 | [diff] [blame] | 163 | it('should: allow users to scout non-existent matches when pre-scouting.', () => { |
| 164 | switchToTab('Entry'); |
| 165 | headerShouldBe(' Team Selection '); |
| 166 | setInputTo('#team_number', '1'); |
| 167 | |
| 168 | // The default team information should be invalid. |
| 169 | cy.contains('button', 'Next').should('be.disabled'); |
| 170 | |
| 171 | // Click the checkmark to designate this as pre-scouting. |
| 172 | // We should now be able to continue scouting. |
| 173 | cy.get('#pre_scouting').click(); |
| 174 | clickButton('Next'); |
| 175 | headerShouldBe('1 Init '); |
| 176 | }); |
| 177 | |
Philipp Schrader | 8702b78 | 2023-04-15 17:33:37 -0700 | [diff] [blame] | 178 | it('should: let users enter match information manually.', () => { |
Philipp Schrader | 75021f5 | 2023-04-09 21:14:13 -0700 | [diff] [blame] | 179 | switchToTab('Entry'); |
Philipp Schrader | 2b33427 | 2023-04-11 21:27:36 -0700 | [diff] [blame] | 180 | headerShouldBe(' Team Selection '); |
Philipp Schrader | 75021f5 | 2023-04-09 21:14:13 -0700 | [diff] [blame] | 181 | |
| 182 | setInputTo('#match_number', '3'); |
| 183 | setInputTo('#team_number', '5254'); |
| 184 | setInputTo('#set_number', '2'); |
Philipp Schrader | 8702b78 | 2023-04-15 17:33:37 -0700 | [diff] [blame] | 185 | cy.get('#comp_level').select('Semi Finals'); |
Philipp Schrader | 75021f5 | 2023-04-09 21:14:13 -0700 | [diff] [blame] | 186 | |
| 187 | clickButton('Next'); |
| 188 | |
| 189 | headerShouldBe('5254 Init '); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 190 | }); |
| 191 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 192 | //TODO(FILIP): Verify last action when the last action header gets added. |
Sabina Leaver | 9b4eb31 | 2023-02-20 19:58:17 -0800 | [diff] [blame] | 193 | it('should: be able to submit data scouting.', () => { |
Filip Kujawa | c1ded37 | 2023-05-27 14:33:43 -0700 | [diff] [blame^] | 194 | submitDataScouting(); |
Philipp Schrader | e11114f | 2023-04-15 17:04:25 -0700 | [diff] [blame] | 195 | |
| 196 | // Now that the data is submitted, the button should be disabled. |
| 197 | switchToTab('Match List'); |
| 198 | cy.get('button.match-item') |
| 199 | .eq(SEMI_FINAL_2_MATCH_3_TEAM_5254) |
| 200 | .should('be.disabled'); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 201 | }); |
| 202 | |
Filip Kujawa | c1ded37 | 2023-05-27 14:33:43 -0700 | [diff] [blame^] | 203 | it('should: be able to delete data scouting entry', () => { |
| 204 | // Submit data to delete. |
| 205 | submitDataScouting(QUALS_MATCH_2_TEAM_4481, 4481); |
| 206 | |
| 207 | switchToTab('View'); |
| 208 | |
| 209 | cy.get('[data-bs-toggle="dropdown"]').click(); |
| 210 | cy.get('[id="stats_source_dropdown"]').click(); |
| 211 | |
| 212 | // Check that table contains data. |
| 213 | cy.get('table.table tbody td').should('contain', '4481'); |
| 214 | |
| 215 | // Find and click the delete button for the row containing team 4481. |
| 216 | cy.get('table.table tbody td') |
| 217 | .contains('4481') |
| 218 | .parent() |
| 219 | .find('[id^="delete_button_"]') |
| 220 | .click(); |
| 221 | cy.on('window:confirm', () => true); |
| 222 | |
| 223 | // Check that deleted data is not in table. |
| 224 | cy.get('table.table tbody').should('not.contain', '4481'); |
| 225 | }); |
| 226 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 227 | it('should: be able to return to correct screen with undo for pick and place.', () => { |
Philipp Schrader | e11114f | 2023-04-15 17:04:25 -0700 | [diff] [blame] | 228 | cy.get('button.match-item').eq(QUALS_MATCH_1_TEAM_3990).click(); |
Philipp Schrader | 2b33427 | 2023-04-11 21:27:36 -0700 | [diff] [blame] | 229 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 230 | // Select Starting Position. |
| 231 | cy.get('[type="radio"]').first().check(); |
| 232 | clickButton('Start Match'); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 233 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 234 | // Pick up cone. |
| 235 | clickButton('CONE'); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 236 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 237 | // Undo that pick up. |
| 238 | clickButton('UNDO'); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 239 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 240 | // User should be back on pickup screen. |
Philipp Schrader | e11114f | 2023-04-15 17:04:25 -0700 | [diff] [blame] | 241 | headerShouldBe('3990 Pickup '); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 242 | |
Filip Kujawa | 2dc9aa6 | 2023-03-04 11:45:01 -0800 | [diff] [blame] | 243 | // Check the same thing but for undoing place. |
| 244 | clickButton('CUBE'); |
| 245 | clickButton('MID'); |
| 246 | clickButton('UNDO'); |
Philipp Schrader | e11114f | 2023-04-15 17:04:25 -0700 | [diff] [blame] | 247 | headerShouldBe('3990 Place '); |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 248 | }); |
| 249 | |
Philipp Schrader | 175a93c | 2023-02-19 13:13:40 -0800 | [diff] [blame] | 250 | it('should: submit note scouting for multiple teams', () => { |
| 251 | // Navigate to Notes Page. |
| 252 | switchToTab('Notes'); |
| 253 | headerShouldBe('Notes'); |
| 254 | |
| 255 | // Add first team. |
| 256 | setInputTo('#team_number_notes', '1234'); |
| 257 | clickButton('Select'); |
| 258 | |
| 259 | // Add note and select keyword for first team. |
| 260 | cy.get('#team-key-1').should('have.text', '1234'); |
| 261 | setInputTo('#text-input-1', 'Good Driving'); |
| 262 | cy.get('#good_driving_0').click(); |
| 263 | |
| 264 | // Navigate to add team selection and add another team. |
| 265 | clickButton('Add team'); |
| 266 | setInputTo('#team_number_notes', '1235'); |
| 267 | clickButton('Select'); |
| 268 | |
| 269 | // Add note and select keyword for second team. |
| 270 | cy.get('#team-key-2').should('have.text', '1235'); |
| 271 | setInputTo('#text-input-2', 'Bad Driving'); |
| 272 | cy.get('#bad_driving_1').click(); |
| 273 | |
| 274 | // Submit Notes. |
| 275 | clickButton('Submit'); |
| 276 | cy.get('#team_number_label').should('have.text', ' Team Number '); |
| 277 | }); |
| 278 | |
| 279 | it('should: switch note text boxes with keyboard shortcuts', () => { |
| 280 | // Navigate to Notes Page. |
| 281 | switchToTab('Notes'); |
| 282 | headerShouldBe('Notes'); |
| 283 | |
| 284 | // Add first team. |
| 285 | setInputTo('#team_number_notes', '1234'); |
| 286 | clickButton('Select'); |
| 287 | |
| 288 | // Add second team. |
| 289 | clickButton('Add team'); |
| 290 | setInputTo('#team_number_notes', '1235'); |
| 291 | clickButton('Select'); |
| 292 | |
| 293 | // Add third team. |
| 294 | clickButton('Add team'); |
| 295 | setInputTo('#team_number_notes', '1236'); |
| 296 | clickButton('Select'); |
| 297 | |
| 298 | for (let i = 1; i <= 3; i++) { |
| 299 | // Press Control + i |
| 300 | cy.get('body').type(`{ctrl}${i}`); |
| 301 | |
| 302 | // Expect text input to be focused. |
| 303 | cy.focused().then(($element) => { |
| 304 | expect($element).to.have.id(`text-input-${i}`); |
| 305 | }); |
| 306 | } |
| 307 | }); |
| 308 | |
| 309 | it('should: submit driver ranking', () => { |
| 310 | // Navigate to Driver Ranking Page. |
| 311 | switchToTab('Driver Ranking'); |
| 312 | headerShouldBe('Driver Ranking'); |
| 313 | |
| 314 | // Input match and team numbers. |
| 315 | setInputTo('#match_number_selection', '11'); |
| 316 | setInputTo('#team_input_0', '123'); |
| 317 | setInputTo('#team_input_1', '456'); |
| 318 | setInputTo('#team_input_2', '789'); |
| 319 | clickButton('Select'); |
| 320 | |
| 321 | // Verify match and team key input. |
| 322 | cy.get('#match_number_heading').should('have.text', 'Match #11'); |
| 323 | cy.get('#team_key_label_0').should('have.text', ' 123 '); |
| 324 | cy.get('#team_key_label_1').should('have.text', ' 456 '); |
| 325 | cy.get('#team_key_label_2').should('have.text', ' 789 '); |
| 326 | |
| 327 | // Rank teams. |
| 328 | cy.get('#up_button_2').click(); |
| 329 | cy.get('#down_button_0').click(); |
| 330 | |
| 331 | // Verify ranking change. |
| 332 | cy.get('#team_key_label_0').should('have.text', ' 789 '); |
| 333 | cy.get('#team_key_label_1').should('have.text', ' 123 '); |
| 334 | cy.get('#team_key_label_2').should('have.text', ' 456 '); |
| 335 | |
| 336 | // Submit. |
| 337 | clickButton('Submit'); |
| 338 | }); |
| 339 | }); |