cypress-plugin-grep-boxes 1.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,11 +6,12 @@
6
6
  A companion Cypress plugin for <code>cy-grep</code> that allows user to run specific test(s) in <code>open</code> mode.
7
7
  </p>
8
8
 
9
- ![Cypress-plugin-grep-boxes](./assets/cypress-plugin-grep-boxes-demo.gif)
9
+ ![Cypress-plugin-grep-boxes](./assets/cy-grep-boxes-demo.gif)
10
10
 
11
11
  ## Features
12
12
 
13
13
  - ✅ A new UI test selection within `cypress open` to filter and run only selected tests in a given spec
14
+ - 🚩 _NEW_ in v2.0.0: Tags are now displayed and can be clicked to filter by respective tag in `cypress open`
14
15
 
15
16
  #### Table of Contents
16
17
 
@@ -27,6 +28,9 @@ A companion Cypress plugin for <code>cy-grep</code> that allows user to run spec
27
28
 
28
29
  1. Install the following packages:
29
30
 
31
+ > [!NOTE]
32
+ > To support `cypress-plugin-grep-boxes` displaying test tags in the Cypress Test Runner, `@bahmutov/cy-grep` must be on version >= 2.1.0
33
+
30
34
  ```sh
31
35
  npm install --save-dev @bahmutov/cy-grep # Dependent package for the plugin
32
36
  npm install --save-dev cypress-plugin-grep-boxes
@@ -34,14 +38,14 @@ npm install --save-dev cypress-plugin-grep-boxes
34
38
 
35
39
  2. In `cypress/support/e2e.js` (For E2E tests) and/or `cypress/support/component.js` (For Component tests),
36
40
 
41
+ > [!IMPORTANT]
42
+ > In the plugin version 2.0.0, the `e2e.js` (or `component.js`) support file import has been updated for simplicity:
43
+
37
44
  ```js
38
- import { greppedTestToggle, addGrepButtons } from 'cypress-plugin-grep-boxes';
45
+ import 'cypress-plugin-grep-boxes';
39
46
  import registerCypressGrep from '@bahmutov/cy-grep/src/support';
40
47
 
41
48
  registerCypressGrep();
42
-
43
- greppedTestToggle();
44
- addGrepButtons();
45
49
  ```
46
50
 
47
51
  ---
@@ -64,7 +68,10 @@ addGrepButtons();
64
68
 
65
69
  ## ✅ Open mode
66
70
 
67
- Within each spec, you can select any given number of suite(s) or individual test(s) and click the filter toggle located on the reporter above:
71
+ Within each spec in Cypress `open` mode:
72
+
73
+ - You can select any given number of individual test(s) and click the filter toggle located on the reporter above
74
+ - You can click on any available tag and run only tests in the spec with the respective tag
68
75
 
69
76
  ![Cypress grep-boxes within UI mode](./assets/grep-boxes-ui.png)
70
77
 
Binary file
@@ -10,12 +10,16 @@ context('Window', () => {
10
10
  cy.window().should('have.property', 'top');
11
11
  });
12
12
 
13
- it('cy.document() - get the document object', () => {
14
- // https://on.cypress.io/document
15
- cy.document().should('have.property', 'charset').and('eq', 'UTF-8');
16
- });
13
+ it(
14
+ 'cy.document() - get the document object',
15
+ { tags: ['@sanity', '@smoke'] },
16
+ () => {
17
+ // https://on.cypress.io/document
18
+ cy.document().should('have.property', 'charset').and('eq', 'UTF-8');
19
+ }
20
+ );
17
21
 
18
- it('cy.title() - get the title', () => {
22
+ it('cy.title() - get the title', { tags: '@title' }, () => {
19
23
  // https://on.cypress.io/title
20
24
  cy.title().should('include', 'Kitchen Sink');
21
25
  });
@@ -15,12 +15,10 @@
15
15
 
16
16
  // Import commands.js using ES2015 syntax:
17
17
  import './commands';
18
- import { greppedTestToggle, addGrepButtons } from '../../index';
18
+ import '../../index';
19
19
  import registerCypressGrep from '@bahmutov/cy-grep/src/support';
20
20
 
21
21
  registerCypressGrep();
22
- greppedTestToggle();
23
- addGrepButtons();
24
22
 
25
23
  // Alternatively you can use CommonJS syntax:
26
24
  // require('./commands')
package/index.js CHANGED
@@ -1,15 +1,12 @@
1
- import registerCypressGrep from '@bahmutov/cy-grep';
2
1
  /**
3
2
  * Adds a toggle to reporter to grep selected tests.
4
3
  */
5
4
 
6
5
  const tests = [];
7
6
 
8
- export const greppedTestToggle = () => {
9
- registerCypressGrep();
10
- const hasStyles = window.top?.document.querySelector('#grepTestToggleStyle');
11
- const hasToggleButton = window.top?.document.querySelector('#grepTestToggle');
12
- const defaultStyles = `
7
+ const hasStyles = window.top?.document.querySelector('#grepTestToggleStyle');
8
+ const hasToggleButton = window.top?.document.querySelector('#grepTestToggle');
9
+ const defaultStyles = `
13
10
  .reporter header {
14
11
  overflow: visible;
15
12
  z-index: 2;
@@ -24,6 +21,7 @@ export const greppedTestToggle = () => {
24
21
  #grepTestToggleControls label {
25
22
  background-color: transparent;
26
23
  padding-top: 5px;
24
+ padding-right: 2px;
27
25
  }
28
26
  #grepTestToggleControls #grepTestToggleTooltip {
29
27
  visibility: hidden;
@@ -34,13 +32,16 @@ export const greppedTestToggle = () => {
34
32
  padding: 5px;
35
33
  border-radius: 3px;
36
34
  position: absolute;
37
- z-index: 1;
38
- top: 27px;
39
- left: 0px;
35
+ z-index: 99999;
36
+ top: 33px;
37
+ right: -2px;
40
38
  height: 28px;
39
+ overflow: visible;
41
40
  }
42
41
  #grepTestToggleControls:hover #grepTestToggleTooltip {
43
42
  visibility: visible;
43
+ z-index: 99999;
44
+ overflow: visible;
44
45
  }
45
46
  #grepTestToggleButton #grepTestToggleLabel {
46
47
  cursor: pointer;
@@ -49,145 +50,203 @@ export const greppedTestToggle = () => {
49
50
  content: " ";
50
51
  position: absolute;
51
52
  bottom: 100%; /* At the top of the tooltip */
52
- right: 85%;
53
+ left: 89%;
54
+ z-index: 99999;
53
55
  margin-left: -5px;
54
56
  border-width: 5px;
55
57
  border-style: solid;
58
+ overflow: visible;
56
59
  border-color: transparent transparent #f3f4fa transparent;
57
60
  }
58
61
  .reporter:has(#grepTestToggle:checked) .command.command-name-request:has(.command-is-event) {
59
62
  display:none
60
63
  }
64
+ .spec-container {
65
+ overflow: visible !important
66
+ }
61
67
  `;
62
- const turnOngrepTestToggleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#afb3c7" class="bi bi-collection-play-fill" viewBox="0 0 16 16">
68
+ const turnOngrepTestToggleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#afb3c7" class="bi bi-collection-play-fill" viewBox="0 0 16 16">
63
69
  <path d="M2.5 3.5a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1zm2-2a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1zM0 13a1.5 1.5 0 0 0 1.5 1.5h13A1.5 1.5 0 0 0 16 13V6a1.5 1.5 0 0 0-1.5-1.5h-13A1.5 1.5 0 0 0 0 6zm6.258-6.437a.5.5 0 0 1 .507.013l4 2.5a.5.5 0 0 1 0 .848l-4 2.5A.5.5 0 0 1 6 12V7a.5.5 0 0 1 .258-.437"/>
64
70
  </svg>`;
65
71
 
66
- const turnOffgrepTestToggleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#afb3c7" class="bi bi-collection-play" viewBox="0 0 16 16">
72
+ const turnOffgrepTestToggleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#afb3c7" class="bi bi-collection-play" viewBox="0 0 16 16">
67
73
  <path d="M2 3a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 0-1h-11A.5.5 0 0 0 2 3m2-2a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 0-1h-7A.5.5 0 0 0 4 1m2.765 5.576A.5.5 0 0 0 6 7v5a.5.5 0 0 0 .765.424l4-2.5a.5.5 0 0 0 0-.848z"/>
68
74
  <path d="M1.5 14.5A1.5 1.5 0 0 1 0 13V6a1.5 1.5 0 0 1 1.5-1.5h13A1.5 1.5 0 0 1 16 6v7a1.5 1.5 0 0 1-1.5 1.5zm13-1a.5.5 0 0 0 .5-.5V6a.5.5 0 0 0-.5-.5h-13A.5.5 0 0 0 1 6v7a.5.5 0 0 0 .5.5z"/>
69
75
  </svg>`;
70
76
 
71
- const turnOffgrepTestToggleDescription = 'Filter selected tests';
72
- const turnOngrepTestToggleDescription = 'Unfilter selected tests';
73
-
74
- // append styles
75
- if (!hasStyles) {
76
- const reporterEl = window.top?.document.querySelector('#unified-reporter');
77
- const reporterStyleEl = document.createElement('style');
78
- reporterStyleEl.setAttribute('id', 'grepTestToggleStyle');
79
- reporterStyleEl.innerHTML = defaultStyles;
80
- reporterEl?.appendChild(reporterStyleEl);
77
+ const turnOffgrepTestToggleDescription = 'Filter selected tests';
78
+ const turnOngrepTestToggleDescription = 'Unfilter selected tests';
79
+
80
+ // append styles
81
+ if (!hasStyles) {
82
+ let reporterEl;
83
+ const reporterStyleEl = document.createElement('style');
84
+ if (Cypress.version >= '15.0.0') {
85
+ reporterEl = window.top?.document.querySelector('.runnable-header');
86
+ } else {
87
+ reporterEl = window.top?.document.querySelector('#unified-reporter');
81
88
  }
89
+ reporterStyleEl.setAttribute('id', 'grepTestToggleStyle');
90
+ reporterStyleEl.innerHTML = defaultStyles;
91
+ reporterEl?.appendChild(reporterStyleEl);
92
+ }
82
93
 
83
- if (!hasToggleButton) {
84
- const header = window.top?.document.querySelector(
85
- '#unified-reporter header'
86
- );
87
- const headerToggleDiv = document.createElement('div');
88
- const headerToggleSpan = document.createElement('span');
89
- const headerToggleTooltip = document.createElement('span');
90
- const headerToggleButton = document.createElement('button');
91
- const headerToggleInput = document.createElement('input');
92
- const headerToggleLabel = document.createElement('label');
93
-
94
- headerToggleInput.setAttribute('type', 'checkbox');
95
-
96
- headerToggleInput.setAttribute('id', 'grepTestToggle');
97
- headerToggleLabel.setAttribute('for', 'grepTestToggle');
98
- headerToggleLabel.setAttribute('id', 'grepTestToggleLabel');
99
- headerToggleLabel.innerHTML = turnOffgrepTestToggleIcon;
100
-
101
- headerToggleDiv.setAttribute('class', 'controls');
102
- headerToggleDiv.setAttribute('id', 'grepTestToggleControls');
103
- headerToggleTooltip.setAttribute('id', 'grepTestToggleTooltip');
104
- headerToggleTooltip.innerText = turnOffgrepTestToggleDescription;
105
- headerToggleButton.setAttribute(
106
- 'aria-label',
107
- turnOffgrepTestToggleDescription
108
- );
109
- headerToggleButton.setAttribute('id', 'grepTestToggleButton');
110
-
111
- header?.appendChild(headerToggleDiv);
112
- headerToggleDiv?.appendChild(headerToggleSpan);
113
- headerToggleDiv?.appendChild(headerToggleTooltip);
114
- headerToggleSpan?.appendChild(headerToggleButton);
115
- headerToggleButton?.appendChild(headerToggleInput);
116
- headerToggleButton?.appendChild(headerToggleLabel);
94
+ if (!hasToggleButton) {
95
+ let header;
96
+ if (Cypress.version >= '15.0.0') {
97
+ // TODO: Cypress v15 GUI provides option for Cypress Studio which pushes the grep toggle button around the UI
98
+ // For simplicity, moving the toggle button to the spec container above the stop button
99
+ header = window.top?.document.querySelector('.runnable-header');
100
+ } else {
101
+ header = window.top?.document.querySelector('#unified-reporter header');
117
102
  }
118
-
119
- const grepTestToggleElement =
120
- window.top?.document.querySelector('#grepTestToggle');
121
- const grepTestToggleLabelElement = window.top?.document.querySelector(
122
- '[for=grepTestToggle]'
123
- );
124
- const grepTestToggleTooltipElement = window.top?.document.querySelector(
125
- '#grepTestToggleTooltip'
103
+ const headerToggleDiv = document.createElement('div');
104
+ const headerToggleSpan = document.createElement('span');
105
+ const headerToggleTooltip = document.createElement('span');
106
+ const headerToggleButton = document.createElement('button');
107
+ const headerToggleInput = document.createElement('input');
108
+ const headerToggleLabel = document.createElement('label');
109
+
110
+ headerToggleInput.setAttribute('type', 'checkbox');
111
+
112
+ headerToggleInput.setAttribute('id', 'grepTestToggle');
113
+ headerToggleLabel.setAttribute('for', 'grepTestToggle');
114
+ headerToggleLabel.setAttribute('id', 'grepTestToggleLabel');
115
+ headerToggleLabel.innerHTML = turnOffgrepTestToggleIcon;
116
+
117
+ headerToggleDiv.setAttribute('id', 'grepTestToggleControls');
118
+ headerToggleTooltip.setAttribute('id', 'grepTestToggleTooltip');
119
+ headerToggleTooltip.innerText = turnOffgrepTestToggleDescription;
120
+ headerToggleButton.setAttribute(
121
+ 'aria-label',
122
+ turnOffgrepTestToggleDescription
126
123
  );
124
+ headerToggleButton.setAttribute('id', 'grepTestToggleButton');
125
+
126
+ headerToggleDiv.setAttribute('class', 'controls');
127
+ header?.appendChild(headerToggleDiv);
128
+ headerToggleDiv?.appendChild(headerToggleSpan);
129
+ headerToggleDiv?.appendChild(headerToggleTooltip);
130
+ headerToggleSpan?.appendChild(headerToggleButton);
131
+ headerToggleButton?.appendChild(headerToggleInput);
132
+ headerToggleButton?.appendChild(headerToggleLabel);
133
+ }
127
134
 
128
- grepTestToggleElement?.addEventListener('change', (e) => {
129
- const stopBtn = window.top?.document.querySelector('.reporter .stop');
130
-
131
- if (e.target.checked) {
132
- if (stopBtn) {
133
- stopBtn.click();
135
+ const grepTestToggleElement =
136
+ window.top?.document.querySelector('#grepTestToggle');
137
+ const grepTestToggleLabelElement = window.top?.document.querySelector(
138
+ '[for=grepTestToggle]'
139
+ );
140
+ const grepTestToggleTooltipElement = window.top?.document.querySelector(
141
+ '#grepTestToggleTooltip'
142
+ );
143
+
144
+ grepTestToggleElement?.addEventListener('change', (e) => {
145
+ const stopBtn = window.top?.document.querySelector('.reporter .stop');
146
+
147
+ if (e.target.checked) {
148
+ if (stopBtn) {
149
+ stopBtn.click();
150
+ }
151
+ // store all checked checkbox values then send to grep in accepted format
152
+ const tests = [
153
+ ...window.top?.document.querySelectorAll('.grep-test-checkbox:checked'),
154
+ ].map((e) => e.value);
155
+ // store all non-checked checkbox values
156
+ const uncheckedTests = [
157
+ ...window.top?.document.querySelectorAll(
158
+ '.grep-test-checkbox:not(:checked)'
159
+ ),
160
+ ].map((e) => e.value);
161
+
162
+ tests.forEach((t) => {
163
+ // if a checked test title begins with the grep inverted '-' symbol, remove the '-'
164
+ if (t[0] === '-') {
165
+ tests.push(t.slice(1));
166
+ tests.splice(
167
+ tests.findIndex((e) => e === t),
168
+ 1
169
+ );
134
170
  }
135
- // store all checked checkbox values then send to grep in accepted format
136
- const tests = [
137
- ...window.top?.document.querySelectorAll('.grep-test-checkbox:checked'),
138
- ].map((e) => e.value);
139
- // store all non-checked checkbox values
140
- const uncheckedTests = [
141
- ...window.top?.document.querySelectorAll(
142
- '.grep-test-checkbox:not(:checked)'
143
- ),
144
- ].map((e) => e.value);
145
-
146
- tests.forEach((t) => {
147
- // if a checked test title begins with the grep inverted '-' symbol, remove the '-'
148
- if (t[0] === '-') {
149
- tests.push(t.slice(1));
150
- tests.splice(
151
- tests.findIndex((e) => e === t),
152
- 1
153
- );
171
+
172
+ // if a non-checked test's title includes a checked test's title, invert grep for unchecked title
173
+ uncheckedTests.forEach((u) => {
174
+ if (u.includes(t)) {
175
+ tests.push(`-${u}`);
154
176
  }
155
- // if a non-checked test's title includes a checked test's title, invert grep for unchecked title
156
- uncheckedTests.forEach((u) => {
157
- if (u.includes(t)) {
158
- tests.push(`-${u}`);
159
- }
160
- });
161
177
  });
178
+ });
162
179
 
163
- Cypress.grep(tests.join(';'));
180
+ Cypress.grep(tests.join(';'));
164
181
 
165
- // when checked, grep only selected tests in spec
166
- grepTestToggleLabelElement.innerHTML = turnOngrepTestToggleIcon;
167
- grepTestToggleTooltipElement.innerHTML = turnOngrepTestToggleDescription;
168
- } else {
169
- if (stopBtn) {
170
- stopBtn.click();
171
- }
172
- // when unchecked, ungrep and show all tests in spec
173
- Cypress.grep();
174
- grepTestToggleLabelElement.innerHTML = turnOffgrepTestToggleIcon;
175
- grepTestToggleTooltipElement.innerHTML = turnOffgrepTestToggleDescription;
182
+ // when checked, grep only selected tests in spec
183
+ grepTestToggleLabelElement.innerHTML = turnOngrepTestToggleIcon;
184
+ grepTestToggleTooltipElement.innerHTML = turnOngrepTestToggleDescription;
185
+ } else {
186
+ if (stopBtn) {
187
+ stopBtn.click();
188
+ }
189
+ // when unchecked, ungrep and show all tests in spec
190
+ Cypress.grep();
191
+ grepTestToggleLabelElement.innerHTML = turnOffgrepTestToggleIcon;
192
+ grepTestToggleTooltipElement.innerHTML = turnOffgrepTestToggleDescription;
193
+ }
194
+ });
195
+
196
+ // Wrapping logic within isInteractive check
197
+ // This targets cypress open mode where user can switch specs
198
+ if (Cypress.config('isInteractive')) {
199
+ Cypress.on('window:unload', () => {
200
+ addTags();
201
+ // Store the current Cypress test runner url
202
+ // This is to check against any spec change in test runner while the grep filter is activated
203
+ // If a user does switch spec while filter is active, the filter will be reset
204
+ const sidebarSpecLinkPage = window.top?.document.querySelector(
205
+ '[data-cy="sidebar-link-specs-page"]'
206
+ );
207
+ const grepTestToggleElement =
208
+ window.top?.document.querySelector('#grepTestToggle');
209
+
210
+ if (
211
+ window.top?.document.URL !=
212
+ sidebarSpecLinkPage.getAttribute('data-url') &&
213
+ grepTestToggleElement.checked
214
+ ) {
215
+ grepTestToggleElement.click();
176
216
  }
217
+
218
+ sidebarSpecLinkPage.setAttribute('data-url', window.top?.document.URL);
177
219
  });
178
- };
220
+ }
221
+
222
+ if (Cypress.config('isInteractive')) {
223
+ if (
224
+ // if the grep test toggle is not checked, do not run tests
225
+ Cypress.env('disableInitialAutoRun') &&
226
+ window.top?.document.querySelectorAll('#grepTestToggle:checked').length ===
227
+ 0
228
+ ) {
229
+ const sidebarSpecLinkPage = window.top?.document.querySelector(
230
+ '[data-cy="sidebar-link-specs-page"]'
231
+ );
232
+
233
+ sidebarSpecLinkPage.setAttribute('data-url', window.top?.document.URL);
234
+ Cypress.runner.stop();
235
+ }
236
+ }
179
237
 
180
238
  /**
181
239
  * Adds a checkbox for each suite and test for run selection.
182
240
  */
183
241
 
184
- export const addGrepButtons = () => {
242
+ const addGrepButtons = () => {
185
243
  const hasStyles = window.top?.document.querySelector('#grepButtonsStyle');
186
244
 
187
245
  const grepTestsBtnClass = 'grep-tests-btn';
188
246
 
189
247
  const defaultStyles = `
190
248
  .grep-tests-btn {
249
+ pointer-events: auto;
191
250
  background: none;
192
251
  color: inherit;
193
252
  padding: 0 20px;
@@ -207,9 +266,16 @@ export const addGrepButtons = () => {
207
266
  runnablesStyleEl.innerHTML = defaultStyles;
208
267
  runnablesEl?.appendChild(runnablesStyleEl);
209
268
  }
210
- const testsAndSuites = window.top?.document.querySelectorAll(
211
- '.test.runnable, .suite.runnable'
212
- );
269
+ let testsAndSuites;
270
+ if (Cypress.version >= '15.0.0') {
271
+ // TODO: Cypress v15 implemented a new suite naming convention that utilizes " > " separator between nested suites
272
+ // For now, only allowing tests to be selected for simplicity
273
+ testsAndSuites = window.top?.document.querySelectorAll('.test.runnable');
274
+ } else {
275
+ testsAndSuites = window.top?.document.querySelectorAll(
276
+ '.test.runnable, .suite.runnable'
277
+ );
278
+ }
213
279
  [...testsAndSuites].forEach((t) => {
214
280
  const header = t.querySelector('.collapsible-header');
215
281
  const title = header.querySelector('.runnable-title');
@@ -259,52 +325,143 @@ export const addGrepButtons = () => {
259
325
 
260
326
  // To prevent a checkbox from expanding a runnable, click the collapsible
261
327
  grepTestsBtn?.addEventListener('change', (e) => {
262
- if (e.target.checked) {
328
+ if (e.target.checked || e.target.unchecked) {
263
329
  header.click();
264
330
  }
265
331
  });
266
332
  });
267
333
  };
268
334
 
269
- // Wrapping logic within isInteractive check
270
- // This targets cypress open mode where user can switch specs
271
- if (Cypress.config('isInteractive')) {
272
- Cypress.on('window:unload', () => {
273
- // Store the current Cypress test runner url
274
- // This is to check against any spec change in test runner while the grep filter is activated
275
- // If a user does switch spec while filter is active, the filter will be reset
276
- const sidebarSpecLinkPage = window.top?.document.querySelector(
277
- '[data-cy="sidebar-link-specs-page"]'
278
- );
279
- const grepTestToggleElement =
280
- window.top?.document.querySelector('#grepTestToggle');
335
+ /**
336
+ * Adds a clickable test tag for each respective test in Cypress open mode.
337
+ */
281
338
 
282
- if (
283
- window.top?.document.URL !=
284
- sidebarSpecLinkPage.getAttribute('data-url') &&
285
- grepTestToggleElement.checked
286
- ) {
287
- grepTestToggleElement.click();
339
+ export const addTags = () => {
340
+ const defaultStyles = `
341
+ [data-attribute="test-tags"]:focus {
342
+ box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.9);;
343
+ outline: none;
344
+ }
345
+ `;
346
+ if (Cypress.env('specTags')) {
347
+ const hasStyles = window.top?.document.querySelector('#tagPillStyle');
348
+
349
+ if (!hasStyles) {
350
+ const runnablesStyleEl = window.top?.document.createElement('style');
351
+ const runnables = window.top?.document.querySelector('.runnables');
352
+ runnablesStyleEl.setAttribute('id', 'tagPillStyle');
353
+ runnablesStyleEl.innerHTML = defaultStyles;
354
+ runnables?.appendChild(runnablesStyleEl);
288
355
  }
289
356
 
290
- sidebarSpecLinkPage.setAttribute('data-url', window.top?.document.URL);
291
- });
357
+ const testsAndSuites =
358
+ window.top?.document.querySelectorAll('.test.runnable');
359
+ [...testsAndSuites].forEach((t) => {
360
+ const header = t.querySelector('.collapsible-header');
361
+ const collapsibleHeaderText = header?.querySelector(
362
+ '.collapsible-header-text'
363
+ );
364
+ if (!collapsibleHeaderText.className.includes('flex flex-wrap gap-2')) {
365
+ collapsibleHeaderText.className += ' flex flex-wrap gap-2';
366
+ }
367
+ const title = header.querySelector('.runnable-title');
368
+ const testName = title.innerText.split('\n')[0];
369
+ const allSpecTags = Cypress.env('specTags');
370
+ const testTag = getTagsForTitle(testName, allSpecTags);
371
+ if (t.querySelectorAll('[data-attribute="test-tags"]').length === 0) {
372
+ renderTagPills(testTag, title);
373
+ }
374
+ });
375
+ }
376
+ };
377
+
378
+ function getTagsForTitle(title, fullTagsObj) {
379
+ const test = fullTagsObj[title];
380
+ if (!test) {
381
+ return []; // Title not found
382
+ }
383
+
384
+ return [...test.effectiveTestTags, ...test.requiredTestTags];
292
385
  }
293
386
 
294
- if (Cypress.config('isInteractive')) {
295
- if (
296
- // if the grep test toggle is not checked, do not run tests
297
- Cypress.env('disableInitialAutoRun') &&
298
- window.top?.document.querySelectorAll('#grepTestToggle:checked').length ===
299
- 0
300
- ) {
301
- const sidebarSpecLinkPage = window.top?.document.querySelector(
302
- '[data-cy="sidebar-link-specs-page"]'
387
+ function renderTagPills(tags, container) {
388
+ tags.forEach((tag) => {
389
+ const pill = document.createElement('button');
390
+ pill.textContent = tag;
391
+ pill.setAttribute('type', 'button');
392
+ pill.setAttribute('aria-label', 'Filter by test tag from grep plugin');
393
+ pill.setAttribute('data-attribute', 'test-tags');
394
+
395
+ const brightColors = [
396
+ '#FF7373', // Red
397
+ '#FFB673', // Orange
398
+ '#FFE673', // Yellow
399
+ '#CFFF73', // Lime
400
+ '#73FF78', // Light Green
401
+ '#73FFC6', // Aqua
402
+ '#73E6FF', // Sky Blue
403
+ '#73A6FF', // Blue
404
+ '#7373FF', // Indigo
405
+ '#B473FF', // Purple
406
+ '#E673FF', // Magenta
407
+ '#FF73B6', // Rose
408
+ '#FF6E6E', // Slightly different Red
409
+ '#FFAB73', // Peach
410
+ '#FFF473', // Pale Yellow
411
+ '#DFFF73', // Pale Lime
412
+ '#73FFB6', // Mint
413
+ '#73DFFF', // Light Blue
414
+ '#A673FF', // Lavender
415
+ ];
416
+
417
+ function tagNameToColor(str) {
418
+ let hash = 0;
419
+ for (let i = 0; i < str.length; i++)
420
+ hash = str.charCodeAt(i) + ((hash << 7) - hash);
421
+ return Math.abs(hash % 6);
422
+ }
423
+
424
+ function getTagColor(tag) {
425
+ const hash = tagNameToColor(tag);
426
+ return brightColors[hash % brightColors.length];
427
+ }
428
+
429
+ const bgColor = getTagColor(tag);
430
+
431
+ pill.setAttribute(
432
+ 'style',
433
+ `
434
+ background: ${bgColor};
435
+ color: black;
436
+ padding: 0.1rem 0.2rem;
437
+ font-size: 0.875rem;
438
+ font-weight: bold;
439
+ border-radius: 2px;
440
+ display: inline-flex;
441
+ align-items: center;
442
+ white-space: nowrap;
443
+ cursor: pointer;
444
+ pointer-events: auto;
445
+ `
303
446
  );
304
447
 
305
- sidebarSpecLinkPage.setAttribute('data-url', window.top?.document.URL);
306
- Cypress.runner.stop();
307
- }
448
+ // Base styles
449
+ pill.className = `px-1 text-sm block inline-flex items-center whitespace-nowrap rounded`;
450
+
451
+ if (
452
+ // if the grep test toggle is checked, do not show checkboxes on each runnable
453
+ window.top?.document.querySelectorAll('#grepTestToggle:checked')
454
+ .length === 0
455
+ ) {
456
+ // Handle selection toggle
457
+ pill.addEventListener('click', () => {
458
+ window.top?.document.querySelector('#grepTestToggle').click();
459
+ Cypress.grep(undefined, tag);
460
+ });
461
+ }
462
+
463
+ container.after(pill);
464
+ });
308
465
  }
309
466
 
310
467
  Cypress.on('test:before:run', () => {
@@ -317,22 +474,24 @@ Cypress.on('test:before:run', () => {
317
474
  }
318
475
  });
319
476
 
320
- // To account for when the collapsible runnables are removed, add back grep buttons
321
- if (
322
- // if the grep test toggle is checked, do not show checkboxes on each runnable
323
- window.top?.document.querySelectorAll('#grepTestToggle:checked').length === 0
324
- ) {
325
- // watching for changes to DOM structure
326
- MutationObserver = window.MutationObserver;
477
+ // To account for when the collapsible runnables are removed, add back tags and grep buttons
478
+ // watching for changes to DOM structure
479
+ MutationObserver = window.MutationObserver;
327
480
 
328
- var observer = new MutationObserver(function () {
481
+ var observer = new MutationObserver(function () {
482
+ if (
483
+ // if the grep test toggle is checked, do not show checkboxes on each runnable
484
+ window.top?.document.querySelectorAll('#grepTestToggle:checked').length ===
485
+ 0
486
+ ) {
329
487
  // fired when a mutation occurs
330
488
  addGrepButtons();
331
- });
489
+ }
490
+ addTags();
491
+ });
332
492
 
333
- // defining the window.top?.document to be observed by the observer
334
- observer.observe(window.top?.document, {
335
- subtree: true,
336
- attributes: true,
337
- });
338
- }
493
+ // defining the window.top?.document to be observed by the observer
494
+ observer.observe(window.top?.document, {
495
+ subtree: true,
496
+ attributes: true,
497
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cypress-plugin-grep-boxes",
3
- "version": "1.1.2",
3
+ "version": "2.0.0",
4
4
  "description": "Cypress plugin that allows user to run specific tests in open mode.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -9,8 +9,8 @@
9
9
  "cypress-plugin"
10
10
  ],
11
11
  "devDependencies": {
12
- "@bahmutov/cy-grep": "^2.0.22",
13
- "cypress": "^14.3.0"
12
+ "@bahmutov/cy-grep": "^2.1.0",
13
+ "cypress": "^15.5.0"
14
14
  },
15
15
  "publishConfig": {
16
16
  "registry": "https://registry.npmjs.org/"