Skip to content

Commit a2e002e

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/mabel/issue-31677-reporter-redesign' into mabel/issue-31677-reporter-redesign
2 parents 4a99f86 + dbcd55d commit a2e002e

31 files changed

+851
-385
lines changed

.circleci/cache-version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Bump this version to force CI to re-create the cache from scratch.
22

3-
6-23-2025
3+
6-26-2025

.circleci/workflows.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: 2.1
22

3-
chrome-stable-version: &chrome-stable-version "137.0.7151.103"
4-
chrome-beta-version: &chrome-beta-version "138.0.7204.23"
3+
chrome-stable-version: &chrome-stable-version "137.0.7151.119"
4+
chrome-beta-version: &chrome-beta-version "138.0.7204.35"
55
firefox-stable-version: &firefox-stable-version "137.0"
66

77
orbs:

cli/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ _Released 07/15/2025 (PENDING)_
1212
- `@cypress/webpack-dev-server` no longer supports `webpack-dev-server` version 4. Addresses [#31605](https://github.com/cypress-io/cypress/issues/31605). If you still need to use `webpack-dev-server` version 4, please see our [migration guide](https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-150).
1313
- In order to better align with best practices, `@cypress/webpack-batteries-included-preprocessor` no longer includes certain browser built-ins that were automatically provided by Webpack 4. The removed built-ins are `assert`, `constants`, `crypto`, `domain`, `events`, `http`, `https`, `punycode`, `querystring`, `string_decoder`, `sys`, `timers`, `tty`, `url`, `util`, `vm`, and `zlib`. However, we know that certain built-ins are popular, given that many users have files that are shared between their Cypress tests and node context. Because of this, `@cypress/webpack-batteries-included-preprocessor` will ship with built-in support for `buffer`, `path`, `process`, `os`, and `stream`. If there is a built-in that isn't supported be default and you need to add support, please refer to the Webpack [resolve.fallback](https://webpack.js.org/configuration/resolve/#resolvefallback) documentation and the [`@cypress/webpack-batteries-included-preprocessor` README](../npm/webpack-batteries-included-preprocessor/README.md). Addresses [#31039](https://github.com/cypress-io/cypress/issues/31039).
1414
- The application under test's `pagehide` event in Chromium browsers will no longer trigger Cypress's `window:unload` event. Addressed in [#31853](https://github.com/cypress-io/cypress/pull/31853).
15+
- The `Cypress.SelectorPlayground` API has been renamed to `Cypress.ElementSelector`. This API was renamed to accommodate its use for defining `selectorPriority` in Cypress Studio and our future [`cy.prompt` release](https://on.cypress.io/cy-prompt-early-access?utm_source=docs&utm_medium=app-changelog&utm_content=cy-prompt-release). Addresses [#31801](https://github.com/cypress-io/cypress/issues/31801). Addressed in [#31889](https://github.com/cypress-io/cypress/pull/31889).
1516
- **Component Testing breaking changes:**
1617
- Removed support for Angular 17. The minimum supported version is now `18.0.0`. Addresses [#31303](https://github.com/cypress-io/cypress/issues/31303).
1718
- `@cypress/angular` now requires a minimum of `zone.js` `0.14.0`. Addresses [#31582](https://github.com/cypress-io/cypress/issues/31582).
@@ -20,6 +21,7 @@ _Released 07/15/2025 (PENDING)_
2021
**Features:**
2122

2223
- [`cy.url()`](https://docs.cypress.io/api/commands/url), [`cy.hash()`](https://docs.cypress.io/api/commands/hash), [`cy.go()`](https://docs.cypress.io/api/commands/go), [`cy.reload()`](https://docs.cypress.io/api/commands/reload), [`cy.title()`](https://docs.cypress.io/api/commands/title), and [`cy.location()`](https://docs.cypress.io/api/commands/location) now use the automation client (CDP for Chromium browsers and WebDriver BiDi for Firefox) to return the appropriate values from the commands to the user instead of the window object. This is to avoid cross origin issues with [`cy.origin()`](https://docs.cypress.io/api/commands/origin) so these commands can be invoked anywhere inside a Cypress test without having to worry about origin access issues. Experimental Webkit still will use the window object to retrieve these values. Also, [`cy.window()`](https://docs.cypress.io/api/commands/window) will always return the current window object, regardless of origin restrictions. Not every property from the window object will be accessible depending on the origin context. Addresses [#31196](https://github.com/cypress-io/cypress/issues/31196).
24+
- Selectors accepted in the `selectorPriority` of the `SelectorPlayground` (renamed to `ElementSelector`) API have been expanded to accept `name` and `attributes:*`. Additionally, the default selector priority used by Cypress now includes `name`. Addresses [#31801](https://github.com/cypress-io/cypress/issues/30309) and [#6876](https://github.com/cypress-io/cypress/issues/6876). Addressed in [#31889](https://github.com/cypress-io/cypress/pull/31889).
2325
- [`tsx`](https://tsx.is/) is now used in all cases to run the Cypress config, replacing [ts-node](https://github.com/TypeStrong/ts-node) for TypeScript and Node for commonjs/ESM. This should allow for more interoperability for users who are using any variant of ES Modules. Addresses [#8090](https://github.com/cypress-io/cypress/issues/8090), [#15724](https://github.com/cypress-io/cypress/issues/15724), [#21805](https://github.com/cypress-io/cypress/issues/21805), [#22273](https://github.com/cypress-io/cypress/issues/22273), [#22747](https://github.com/cypress-io/cypress/issues/22747), [#23141](https://github.com/cypress-io/cypress/issues/23141), [#25958](https://github.com/cypress-io/cypress/issues/25958), [#25959](https://github.com/cypress-io/cypress/issues/25959), [#26606](https://github.com/cypress-io/cypress/issues/26606), [#27359](https://github.com/cypress-io/cypress/issues/27359), [#27450](https://github.com/cypress-io/cypress/issues/27450), [#28442](https://github.com/cypress-io/cypress/issues/28442), [#30318](https://github.com/cypress-io/cypress/issues/30318), [#30718](https://github.com/cypress-io/cypress/issues/30718), [#30907](https://github.com/cypress-io/cypress/issues/30907), [#30915](https://github.com/cypress-io/cypress/issues/30915), [#30925](https://github.com/cypress-io/cypress/issues/30925), [#30954](https://github.com/cypress-io/cypress/issues/30954) and [#31185](https://github.com/cypress-io/cypress/issues/31185).
2426

2527
**Misc:**
@@ -38,6 +40,7 @@ _Released 6/17/2025_
3840
**Bugfixes:**
3941

4042
- Fixed an issue when using `Cypress.stop()` where a run may be aborted prior to receiving the required runner events causing Test Replay to not be available. Addresses [#31781](https://github.com/cypress-io/cypress/issues/31781).
43+
- Fixed an issue where prerequests with Firefox BiDi were prematurely being removed or matched incorrectly. Addresses [#31482](https://github.com/cypress-io/cypress/issues/31482).
4144

4245
## 14.4.1
4346

cli/types/cypress.d.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -696,10 +696,21 @@ declare namespace Cypress {
696696
}
697697

698698
/**
699-
* @see https://on.cypress.io/selector-playground-api
699+
* Element selector logic used for generating selectors for elements
700+
* in the Selector Playground and Cypress Studio.
701+
* @see https://on.cypress.io/element-selector-api
702+
*/
703+
ElementSelector: {
704+
defaults(options: Partial<ElementSelectorDefaultsOptions>): void
705+
getSelector($el: JQuery): JQuery.Selector
706+
}
707+
708+
/**
709+
* @deprecated Use ElementSelector instead
710+
* @see https://on.cypress.io/element-selector-api
700711
*/
701712
SelectorPlayground: {
702-
defaults(options: Partial<SelectorPlaygroundDefaultsOptions>): void
713+
defaults(options: Partial<ElementSelectorDefaultsOptions>): void
703714
getSelector($el: JQuery): JQuery.Selector
704715
}
705716

@@ -2971,7 +2982,7 @@ declare namespace Cypress {
29712982
*/
29722983
requestTimeout: number
29732984
/**
2974-
* Time, in milliseconds, to wait until a response in a [cy.request()](https://on.cypress.io/request), [cy.wait()](https://on.cypress.io/wait), [cy.fixture()](https://on.cypress.io/fixture), [cy.getCookie()](https://on.cypress.io/getcookie), [cy.getCookies()](https://on.cypress.io/getcookies), [cy.setCookie()](https://on.cypress.io/setcookie), [cy.clearCookie()](https://on.cypress.io/clearcookie), [cy.clearCookies()](https://on.cypress.io/clearcookies), and [cy.screenshot()](https://on.cypress.io/screenshot) commands
2985+
* Time, in milliseconds, to wait for a response in a [cy.request()](https://on.cypress.io/request), [cy.wait()](https://on.cypress.io/wait), [cy.fixture()](https://on.cypress.io/fixture), [cy.getCookie()](https://on.cypress.io/getcookie), [cy.getCookies()](https://on.cypress.io/getcookies), [cy.setCookie()](https://on.cypress.io/setcookie), [cy.clearCookie()](https://on.cypress.io/clearcookie), [cy.clearCookies()](https://on.cypress.io/clearcookies), and [cy.screenshot()](https://on.cypress.io/screenshot) commands
29752986
* @default 30000
29762987
*/
29772988
responseTimeout: number
@@ -3714,9 +3725,18 @@ declare namespace Cypress {
37143725
screenshotOnRunFailure: boolean
37153726
}
37163727

3717-
interface SelectorPlaygroundDefaultsOptions {
3718-
selectorPriority: string[]
3719-
onElement: ($el: JQuery) => string | null | undefined
3728+
type SelectorPriority =
3729+
| `attribute:${string}`
3730+
| 'attributes'
3731+
| 'class'
3732+
| `data-${string}`
3733+
| 'id'
3734+
| 'name'
3735+
| 'nth-child'
3736+
| 'tag'
3737+
3738+
interface ElementSelectorDefaultsOptions {
3739+
selectorPriority?: SelectorPriority[]
37203740
}
37213741

37223742
interface ScrollToOptions extends Loggable, Timeoutable {

packages/app/src/runner/aut-iframe.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ export class AutIframe {
385385

386386
const Cypress = this.eventManager.getCypress()
387387

388-
const selector = Cypress.SelectorPlayground.getSelector($el)
388+
const selector = Cypress.ElementSelector.getSelector($el)
389389
const selectorPlaygroundStore = useSelectorPlaygroundStore()
390390

391391
this._addOrUpdateSelectorPlaygroundHighlight({

packages/app/src/store/studio-store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ export const useStudioStore = defineStore('studioRecorder', {
383383
if (name === 'click' && this._matchPreviousMouseEvent($el)) {
384384
selector = this._previousMouseEvent?.selector
385385
} else {
386-
selector = getCypress().SelectorPlayground.getSelector($el)
386+
selector = getCypress().ElementSelector.getSelector($el)
387387
}
388388

389389
this._clearPreviousMouseEvent()
@@ -440,7 +440,7 @@ export const useStudioStore = defineStore('studioRecorder', {
440440

441441
_addAssertion ($el: HTMLElement | JQuery<HTMLElement>, ...args: AssertionArgs) {
442442
const id = this._getId()
443-
const selector = getCypress().SelectorPlayground.getSelector($el)
443+
const selector = getCypress().ElementSelector.getSelector($el)
444444

445445
const log: StudioLog = {
446446
id,
@@ -666,7 +666,7 @@ export const useStudioStore = defineStore('studioRecorder', {
666666
if (!this._matchPreviousMouseEvent(target)) {
667667
this._previousMouseEvent = {
668668
element: target,
669-
selector: getCypress().SelectorPlayground.getSelector(window.UnifiedRunner.CypressJQuery(target)),
669+
selector: getCypress().ElementSelector.getSelector(window.UnifiedRunner.CypressJQuery(target)),
670670
}
671671
}
672672
},
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/// <reference types="cypress" />
2+
import type { ElementSelectorAPI } from '../../../src/cypress/element_selector'
3+
import { DEFAULT_SELECTOR_PRIORITIES } from '../../../src/cypress/element_selector'
4+
const { $: $cypress } = Cypress.$Cypress
5+
const ElementSelector = Cypress.ElementSelector as ElementSelectorAPI
6+
7+
const SELECTOR_DEFAULTS: Cypress.SelectorPriority[] = [...DEFAULT_SELECTOR_PRIORITIES]
8+
9+
describe('src/cypress/element_selector', () => {
10+
beforeEach(() => {
11+
ElementSelector.reset()
12+
})
13+
14+
it('has defaults', () => {
15+
expect(ElementSelector.getSelectorPriority()).to.deep.eq(SELECTOR_DEFAULTS)
16+
})
17+
18+
context('.defaults', () => {
19+
it('is noop if not called with selectorPriority', () => {
20+
ElementSelector.defaults({})
21+
expect(ElementSelector.getSelectorPriority()).to.deep.eq(SELECTOR_DEFAULTS)
22+
})
23+
24+
it('sets element:selector:priority if selectorPriority specified', () => {
25+
const selectorPriority: Cypress.SelectorPriority[] = [
26+
'data-1',
27+
'data-2',
28+
'id',
29+
'class',
30+
'tag',
31+
'attributes',
32+
'nth-child',
33+
'name',
34+
'attribute:aria-label',
35+
'attribute:aria-labelledby',
36+
]
37+
38+
ElementSelector.defaults({
39+
selectorPriority,
40+
})
41+
42+
expect(ElementSelector.getSelectorPriority()).to.eql(selectorPriority)
43+
})
44+
45+
it('throws if not passed an object', () => {
46+
const fn = () => {
47+
ElementSelector.defaults(undefined as any)
48+
}
49+
50+
expect(fn).to.throw()
51+
.with.property('message')
52+
.and.include('`Cypress.ElementSelector.defaults()` must be called with an object. You passed: ')
53+
54+
expect(fn).to.throw()
55+
.with.property('docsUrl')
56+
.and.include('https://on.cypress.io/element-selector-api')
57+
})
58+
59+
it('throws if selectorPriority is not an array', () => {
60+
const fn = () => {
61+
ElementSelector.defaults({ selectorPriority: 'foo' as any })
62+
}
63+
64+
expect(fn).to.throw()
65+
.with.property('message')
66+
.and.include('`Cypress.ElementSelector.defaults()` called with invalid `selectorPriority` property. It must be an array. You passed: `foo`')
67+
68+
expect(fn).to.throw()
69+
.with.property('docsUrl')
70+
.and.include('https://on.cypress.io/element-selector-api')
71+
})
72+
73+
it('throws if selectorPriority contains an unsupported priority', () => {
74+
const fn = () => {
75+
ElementSelector.defaults({
76+
selectorPriority: [
77+
'id',
78+
// @ts-expect-error - invalid priority
79+
'attr',
80+
],
81+
})
82+
}
83+
84+
expect(fn).to.throw()
85+
.with.property('message')
86+
.and.include('`Cypress.ElementSelector.defaults()` called with invalid `selectorPriority` property. It must be one of: `data-*`, `attribute:*`, `id`, `class`, `tag`, `name`,`attributes`, or `nth-child`. You passed: `attr`')
87+
})
88+
89+
it('throws if selectorPriority has an unsupported priority that contains a substring of a valid priority', () => {
90+
const fn = () => {
91+
ElementSelector.defaults({
92+
selectorPriority: [
93+
// @ts-expect-error - invalid priority
94+
'idIsNotValid',
95+
],
96+
})
97+
}
98+
99+
expect(fn).to.throw()
100+
.with.property('message')
101+
.and.include('`Cypress.ElementSelector.defaults()` called with invalid `selectorPriority` property. It must be one of: `data-*`, `attribute:*`, `id`, `class`, `tag`, `name`,`attributes`, or `nth-child`. You passed: `idIsNotValid`')
102+
})
103+
})
104+
105+
context('.getSelector', () => {
106+
it('uses defaults.selectorPriority', () => {
107+
const $div = $cypress('<div data-cy=\'main button 123\' data-foo-bar-baz=\'quux\' data-test=\'qwerty\' data-foo=\'bar\' />')
108+
109+
Cypress.$('body').append($div)
110+
111+
expect(ElementSelector.getSelector($div)).to.eq('[data-cy="main button 123"]')
112+
113+
ElementSelector.defaults({
114+
selectorPriority: ['data-foo'],
115+
})
116+
117+
expect(ElementSelector.getSelector($div)).to.eq('[data-foo="bar"]')
118+
})
119+
})
120+
121+
describe('Cypress.SelectorPlayground (renamed)', () => {
122+
it('throws error when calling defaults()', () => {
123+
const fn = () => {
124+
Cypress.SelectorPlayground.defaults({})
125+
}
126+
127+
expect(fn).to.throw()
128+
.with.property('message')
129+
.and.include('`Cypress.SelectorPlayground.defaults()` has been renamed to `Cypress.ElementSelector.defaults()`')
130+
131+
expect(fn).to.throw()
132+
.with.property('message')
133+
.and.include('Please update your code to use `Cypress.ElementSelector` instead')
134+
})
135+
136+
it('throws error when calling getSelector()', () => {
137+
const fn = () => {
138+
Cypress.SelectorPlayground.getSelector($cypress('body'))
139+
}
140+
141+
expect(fn).to.throw()
142+
.with.property('message')
143+
.and.include('`Cypress.SelectorPlayground.getSelector()` has been renamed to `Cypress.ElementSelector.getSelector()`')
144+
145+
expect(fn).to.throw()
146+
.with.property('message')
147+
.and.include('Please update your code to use `Cypress.ElementSelector` instead')
148+
})
149+
})
150+
})

0 commit comments

Comments
 (0)