diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 8ca5300c2095..323ed6f5e7d3 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -757,7 +757,7 @@ commands: command: description: Test command to run to start Cypress tests type: string - default: "yarn cypress:run" + default: "CYPRESS_INTERNAL_ENABLE_TELEMETRY=1 CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY CYPRESS_PROJECT_ID=ypt4pf yarn cypress:run" # if the repo to clone and test is a monorepo, you can # run tests inside a specific subfolder folder: @@ -827,7 +827,7 @@ commands: name: Run tests using browser "<>" working_directory: /tmp/<>/<> command: | - <> -- --browser <> + <> --browser <> --record false - unless: condition: <> steps: @@ -844,7 +844,7 @@ commands: - run: name: Run tests using browser "<>" working_directory: /tmp/<> - command: <> -- --browser <> + command: <> --browser <> --record false - unless: condition: <> steps: @@ -1949,20 +1949,6 @@ jobs: command: yarn workspace @cypress/mount-utils build - store-npm-logs - npm-xpath: - <<: *defaults - resource_class: small - steps: - - restore_cached_workspace - - run: - name: Run tests - command: yarn workspace @cypress/xpath cy:run - - store_test_results: - path: npm/xpath/test_results - - store_artifacts: - path: npm/xpath/test_results - - store-npm-logs - npm-grep: <<: *defaults resource_class: small @@ -2772,6 +2758,7 @@ linux-x64-workflow: &linux-x64-workflow requires: - create-build-artifacts - test-binary-against-cypress-realworld-app: + context: test-runner:cypress-record-key <<: *mainBuildFilters requires: - create-build-artifacts diff --git a/.github/workflows/stale_issues_and_pr_cleanup.yml b/.github/workflows/stale_issues_and_pr_cleanup.yml index 444fefc26cbb..ece421f0aad6 100644 --- a/.github/workflows/stale_issues_and_pr_cleanup.yml +++ b/.github/workflows/stale_issues_and_pr_cleanup.yml @@ -9,7 +9,7 @@ on: max-operations-per-run: description: 'max operations per run' required: false - default: 800 + default: 3000 days-before-stale: description: 'days-before-stale' required: false @@ -32,8 +32,8 @@ permissions: issues: write pull-requests: write env: - DEFAULT_DEBUG_ONLY: true - DEFAULT_MAX_OPS: 800 + DEFAULT_DEBUG_ONLY: false + DEFAULT_MAX_OPS: 3000 DEFAULT_DAYS_BEFORE_STALE: 180 DEFAULT_DAYS_BEFORE_CLOSE: 14 DEFAULT_EXEMPT_ISSUE_LABELS: 'type: feature,type: enhancement,routed-to-e2e,routed-to-ct,routed-to-tools,routed-to-cloud,prevent-stale,triaged' diff --git a/.github/workflows/triage_add_to_project.yml b/.github/workflows/triage_add_to_project.yml index 841d9d3cb58b..11c27119b66d 100644 --- a/.github/workflows/triage_add_to_project.yml +++ b/.github/workflows/triage_add_to_project.yml @@ -6,6 +6,10 @@ on: secrets: ADD_TO_TRIAGE_BOARD_TOKEN: required: true + TRIAGE_BOARD_TOKEN: + required: true + WORKFLOW_DEPLOY_KEY: + required: true issues: types: - opened diff --git a/.gitignore b/.gitignore index f5990e56d99c..b6583eab0c39 100644 --- a/.gitignore +++ b/.gitignore @@ -78,8 +78,6 @@ system-tests/lib/fixtureDirs.ts # from npm/webpack-dev-server /npm/webpack-dev-server/cypress/videos -# from npm/xpath -/npm/xpath/cypress/videos # from npm/grep /npm/grep/cypress/videos diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fe28547aa15..becc1f915c87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,4 +14,3 @@ - [`@cypress/webpack-batteries-included-preprocessor`](https://github.com/cypress-io/cypress/blob/develop/npm/webpack-batteries-included-preprocessor/CHANGELOG.md) - [`@cypress/webpack-dev-server`](https://github.com/cypress-io/cypress/blob/develop/npm/webpack-dev-server/CHANGELOG.md) - [`@cypress/webpack-preprocessor`](https://github.com/cypress-io/cypress/blob/develop/npm/webpack-preprocessor/CHANGELOG.md) -- [`@cypress/xpath`](https://github.com/cypress-io/cypress/blob/develop/npm/xpath/CHANGELOG.md) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e73e5d6a93f4..09763aa5403e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -197,7 +197,6 @@ Here is a list of the npm packages in this repository: | [webpack-batteries-included-preprocessor](./npm/webpack-batteries-included-preprocessor) | `@cypress/webpack-batteries-included-preprocessor` | Cypress preprocessor for bundling JavaScript via webpack with dependencies included and support for various ES features, TypeScript, and CoffeeScript. | | [webpack-dev-server](./npm/webpack-dev-server) | `@cypress/webpack-dev-server` | Webpack powered dev server for Component Testing. | | [webpack-preprocessor](./npm/webpack-preprocessor) | `@cypress/webpack-preprocessor` | Cypress preprocessor for bundling JavaScript via webpack. | - | [xpath](./npm/xpath) | `@cypress/xpath` | Adds XPath command to Cypress.io test runner | We try to tag all issues with a `pkg/` or `npm/` tag describing the appropriate package the work is required in. For public packages, we use their qualified package name: For example, issues relating to the webpack preprocessor are tagged under [`npm: @cypress/webpack-preprocessor`](https://github.com/cypress-io/cypress/labels/npm%3A%20%40cypress%2Fwebpack-preprocessor) label and issues related to the `driver` package are tagged with the [`pkg/driver`](https://github.com/cypress-io/cypress/labels/pkg%2Fdriver) label. diff --git a/README.md b/README.md index 8df5cd1a3ef7..9867193933f2 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,8 @@ npm - - Gitter chat + + Discord chat StackShare diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 86af3b232596..08e2b4265420 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,7 +1,11 @@ -## 12.12.1 +## 12.13.0 -_Released 05/23/2023 (PENDING)_ +_Released 05/23/2023_ + +**Features:** + +- Adds Git-related messages for the [Runs page](https://docs.cypress.io/guides/core-concepts/cypress-app#Runs) and [Debug page](https://docs.cypress.io/guides/cloud/runs#Debug) when users aren't using Git or there are no recorded runs for the current branch. Fixes [#26680](https://github.com/cypress-io/cypress/issues/26680). **Features:** @@ -9,14 +13,17 @@ _Released 05/23/2023 (PENDING)_ **Bugfixes:** -- Reverted [#26452](https://github.com/cypress-io/cypress/pull/26630) which introduced a bug that prevents users from using End to End with Yarn 3. Fixed in [#26735](https://github.com/cypress-io/cypress/pull/26735). Fixes [#26676](https://github.com/cypress-io/cypress/issues/26676). +- Reverted [#26452](https://github.com/cypress-io/cypress/pull/26452) which introduced a bug that prevents users from using End to End with Yarn 3. Fixed in [#26735](https://github.com/cypress-io/cypress/pull/26735). Fixes [#26676](https://github.com/cypress-io/cypress/issues/26676). - Moved `types` condition to the front of `package.json#exports` since keys there are meant to be order-sensitive. Fixed in [#26630](https://github.com/cypress-io/cypress/pull/26630). - Fixed an issue where newly-installed dependencies would not be detected during Component Testing setup. Addresses [#26685](https://github.com/cypress-io/cypress/issues/26685). - Fixed a UI regression that was flashing an "empty" state inappropriately when loading the Debug page. Fixed in [#26761](https://github.com/cypress-io/cypress/pull/26761). +- Fixed an issue in Component Testing setup where TypeScript version 5 was not properly detected. Fixes [#26204](https://github.com/cypress-io/cypress/issues/26204). **Misc:** - Updated styling & content of Cypress Cloud slideshows when not logged in or no runs have been recorded. Addresses [#26181](https://github.com/cypress-io/cypress/issues/26181). +- Changed the nomenclature of 'processing' to 'compressing' when terminal video output is printed during a run. Addresses [#26657](https://github.com/cypress-io/cypress/issues/26657). +- Changed the nomenclature of 'Upload Results' to 'Uploading Screenshots & Videos' when terminal output is printed during a run. Addresses [#26759](https://github.com/cypress-io/cypress/issues/26759). ## 12.12.0 diff --git a/npm/vite-plugin-cypress-esm/CHANGELOG.md b/npm/vite-plugin-cypress-esm/CHANGELOG.md index b27bdd0a39f4..bb4139b30369 100644 --- a/npm/vite-plugin-cypress-esm/CHANGELOG.md +++ b/npm/vite-plugin-cypress-esm/CHANGELOG.md @@ -1,3 +1,10 @@ +# [@cypress/vite-plugin-cypress-esm-v1.0.1](https://github.com/cypress-io/cypress/compare/@cypress/vite-plugin-cypress-esm-v1.0.0...@cypress/vite-plugin-cypress-esm-v1.0.1) (2023-05-23) + + +### Bug Fixes + +* Vite esm plugin issues ([#26714](https://github.com/cypress-io/cypress/issues/26714)) ([38b9526](https://github.com/cypress-io/cypress/commit/38b952622e034928df0d6e5d571e83c22d98ce86)) + # @cypress/vite-plugin-cypress-esm-v1.0.0 (2023-05-04) diff --git a/npm/xpath/.eslintrc b/npm/xpath/.eslintrc deleted file mode 100644 index 2979c0d4f355..000000000000 --- a/npm/xpath/.eslintrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "plugins": [ - "cypress" - ], - "extends": [ - "plugin:@cypress/dev/tests" - ], - "env": { - "cypress/globals": true - }, - "rules": { - "no-console": "off" - } -} diff --git a/npm/xpath/.releaserc.js b/npm/xpath/.releaserc.js deleted file mode 100644 index 17d3bb871472..000000000000 --- a/npm/xpath/.releaserc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('../../.releaserc'), -} diff --git a/npm/xpath/CHANGELOG.md b/npm/xpath/CHANGELOG.md deleted file mode 100644 index 36e869b7cb81..000000000000 --- a/npm/xpath/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# [@cypress/xpath-v2.0.3](https://github.com/cypress-io/cypress/compare/@cypress/xpath-v2.0.2...@cypress/xpath-v2.0.3) (2022-10-26) - - -### Bug Fixes - -* **xpath:** update xpath main path ([#24259](https://github.com/cypress-io/cypress/issues/24259)) ([edf99c4](https://github.com/cypress-io/cypress/commit/edf99c41d6031c7a72ad2258f4fd29231823790c)) - -# [@cypress/xpath-v2.0.2](https://github.com/cypress-io/cypress/compare/@cypress/xpath-v2.0.1...@cypress/xpath-v2.0.2) (2022-09-29) - - -### Bug Fixes - -* release svelte ([b86403f](https://github.com/cypress-io/cypress/commit/b86403fcbcc85ce5be1ca96bbf42357dd24c07dd)) diff --git a/npm/xpath/README.md b/npm/xpath/README.md index 339fce4e3500..de39c50328c9 100644 --- a/npm/xpath/README.md +++ b/npm/xpath/README.md @@ -1,82 +1,3 @@ # @cypress/xpath -> Adds XPath command to [Cypress.io](https://www.cypress.io) test runner - -## Install with npm - -```shell -npm install -D @cypress/xpath -``` - -## Install with Yarn - -```shell -yarn add @cypress/xpath --dev -``` - -Then include in your project's [support file](https://on.cypress.io/support-file) - -```js -require('@cypress/xpath'); -``` - -## Use - -After installation your `cy` object will have `xpath` command. - -```js -it('finds list items', () => { - cy.xpath('//ul[@class="todo-list"]//li').should('have.length', 3); -}); -``` - -You can also chain `xpath` off of another command. - -```js -it('finds list items', () => { - cy.xpath('//ul[@class="todo-list"]').xpath('./li').should('have.length', 3); -}); -``` - -As with other cy commands, it is scoped by `cy.within()`. - -```js -it('finds list items', () => { - cy.xpath('//ul[@class="todo-list"]').within(() => { - cy.xpath('./li').should('have.length', 3); - }); -}); -``` - -**note:** you can test XPath expressions from DevTools console using `$x(...)` function, for example `$x('//div')` to find all divs. - -See [cypress/e2e/spec.cy.js](cypress/e2e/spec.cy.js) - -## Beware the XPath // trap - -In XPath the expression // means something very specific, and it might not be what you think. Contrary to common belief, // means "anywhere in the document" not "anywhere in the current context". As an example: - -```js -cy.xpath('//body').xpath('//script'); -``` - -You might expect this to find all script tags in the body, but actually, it finds all script tags in the entire document, not only those in the body! What you're looking for is the .// expression which means "any descendant of the current node": - -```js -cy.xpath('//body').xpath('.//script'); -``` - -The same thing goes for within: - -```js -cy.xpath('//body').within(() => { - cy.xpath('.//script'); -}); -``` - - -For more, see [Intelligent Code Completion](https://on.cypress.io/intellisense) - -## License - -This project is licensed under the terms of the [MIT license](/LICENSE.md). +> @cypress/xpath has been deprecated and is no longer supported. diff --git a/npm/xpath/cypress.config.js b/npm/xpath/cypress.config.js deleted file mode 100644 index e7a2ffb6dcaa..000000000000 --- a/npm/xpath/cypress.config.js +++ /dev/null @@ -1,12 +0,0 @@ -const { defineConfig } = require('cypress') - -module.exports = defineConfig({ - e2e: { - excludeSpecPattern: '*.html', - supportFile: 'cypress/support/e2e.js', - }, - component: { - excludeSpecPattern: '*.html', - supportFile: 'cypress/support/e2e.js', - }, -}) diff --git a/npm/xpath/cypress/e2e/index.html b/npm/xpath/cypress/e2e/index.html deleted file mode 100644 index ebe8a49935f3..000000000000 --- a/npm/xpath/cypress/e2e/index.html +++ /dev/null @@ -1,26 +0,0 @@ - -
-

cypress-xpath

- -
- - -
-
- - \ No newline at end of file diff --git a/npm/xpath/cypress/e2e/spec.cy.js b/npm/xpath/cypress/e2e/spec.cy.js deleted file mode 100644 index c88881f93a80..000000000000 --- a/npm/xpath/cypress/e2e/spec.cy.js +++ /dev/null @@ -1,183 +0,0 @@ -/// -/// - -describe('cypress-xpath', () => { - it('adds xpath command', () => { - expect(cy).property('xpath').to.be.a('function') - }) - - context('elements', () => { - beforeEach(() => { - cy.visit('cypress/e2e/index.html') - }) - - it('finds h1', () => { - cy.xpath('//h1').should('have.length', 1) - }) - - it('returns jQuery wrapped elements', () => { - cy.xpath('//h1').then((el$) => { - expect(el$).to.have.property('jquery') - }) - }) - - it('returns primitives as is', () => { - cy.xpath('string(//h1)').then((el$) => { - expect(el$).to.not.have.property('jquery') - }) - }) - - it('provides jQuery wrapped elements to assertions', () => { - cy.xpath('//h1').should((el$) => { - expect(el$).to.have.property('jquery') - }) - }) - - it('gets h1 text', () => { - cy.xpath('//h1/text()') - .its('0.textContent') - .should('equal', 'cypress-xpath') - }) - - it('retries until element is inserted', () => { - // the element will be inserted after 1 second - cy.xpath('string(//*[@id="inserted"])').should('equal', 'inserted text') - }) - - describe('chaining', () => { - it('finds h1 within main', () => { - // first assert that h1 doesn't exist as a child of the implicit document subject - cy.xpath('./h1').should('not.exist') - - cy.xpath('//main').xpath('./h1').should('exist') - }) - - it('finds body outside of main when succumbing to // trap', () => { - // first assert that body doesn't actually exist within main - cy.xpath('//main').xpath('.//body').should('not.exist') - - cy.xpath('//main').xpath('//body').should('exist') - }) - - it('finds h1 within document', () => { - cy.document().xpath('//h1').should('exist') - }) - - it('throws when subject is more than a single element', (done) => { - cy.on('fail', (err) => { - expect(err.message).to.eq( - 'xpath() can only be called on a single element. Your subject contained 2 elements.', - ) - - done() - }) - - cy.get('main, div').xpath('foo') - }) - }) - - describe('within()', () => { - it('finds h1 within within-subject', () => { - // first assert that h1 doesn't exist as a child of the implicit document subject - cy.xpath('./h1').should('not.exist') - - cy.xpath('//main').within(() => { - cy.xpath('./h1').should('exist') - }) - }) - - it('finds body outside of within-subject when succumbing to // trap', () => { - // first assert that body doesn't actually exist within main - cy.xpath('//main').within(() => { - cy.xpath('.//body').should('not.exist') - }) - - cy.xpath('//main').within(() => { - cy.xpath('//body').should('exist') - }) - }) - }) - - describe('primitives', () => { - it('counts h1 elements', () => { - cy.xpath('count(//h1)').should('equal', 1) - }) - - it('returns h1 text content', () => { - cy.xpath('string(//h1)').should('equal', 'cypress-xpath') - }) - - it('returns boolean', () => { - cy.xpath('boolean(//h1)').should('be.true') - cy.xpath('boolean(//h2)').should('be.false') - }) - }) - - describe('typing', () => { - it('works on text input', () => { - cy.xpath('//*[@id="name"]').type('World') - cy.contains('span#greeting', 'Hello, World') - }) - }) - - describe('clicking', () => { - it('on button', () => { - // this button invokes window.alert when clicked - const alert = cy.stub() - - cy.on('window:alert', alert) - cy.xpath('//*[@id="first-button"]') - .click() - .then(() => { - expect(alert).to.have.been.calledOnce - }) - }) - }) - }) - - context('logging', () => { - beforeEach(() => { - cy.visit('cypress/e2e/index.html') - }) - - it('should log by default', () => { - cy.spy(Cypress, 'log').log(false) - - cy.xpath('//h1').then(() => { - expect(Cypress.log).to.be.calledWithMatch({ name: 'xpath' }) - }) - }) - - it('logs the selector when not found', (done) => { - cy.xpath('//h1') // does exist - cy.on('fail', (e) => { - const isExpectedErrorMessage = (message) => { - return message.includes('Timed out retrying') && - message.includes( - 'Expected to find element: `//h2`, but never found it.', - ) - } - - if (!isExpectedErrorMessage(e.message)) { - console.error('Cypress test failed with an unexpected error message') - console.error(e) - - return done(e) - } - - // no errors, the error message for not found selector is correct - done() - }) - - cy.xpath('//h2', { timeout: 100 }) // does not exist - }) - - it('should not log when provided log: false', () => { - cy.spy(Cypress, 'log').log(false) - - cy.xpath('//h1', { log: false }).then(() => { - expect(Cypress.log).to.not.be.calledWithMatch({ name: 'xpath' }) - }) - }) - }) -}) diff --git a/npm/xpath/cypress/fixtures/example.json b/npm/xpath/cypress/fixtures/example.json deleted file mode 100644 index da18d9352a17..000000000000 --- a/npm/xpath/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/npm/xpath/cypress/support/e2e.js b/npm/xpath/cypress/support/e2e.js deleted file mode 100644 index 10ea61f87d67..000000000000 --- a/npm/xpath/cypress/support/e2e.js +++ /dev/null @@ -1,16 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -import '../../src' diff --git a/npm/xpath/images/cypress-xpath-reference.gif b/npm/xpath/images/cypress-xpath-reference.gif deleted file mode 100644 index 97ebfa8f41d1..000000000000 Binary files a/npm/xpath/images/cypress-xpath-reference.gif and /dev/null differ diff --git a/npm/xpath/package.json b/npm/xpath/package.json deleted file mode 100644 index c4b72bb9ea29..000000000000 --- a/npm/xpath/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@cypress/xpath", - "version": "0.0.0-development", - "description": "Adds XPath command to Cypress.io test runner", - "main": "src", - "scripts": { - "lint": "eslint . --ext .ts,.js", - "cy:run": "node ../../scripts/cypress.js run --e2e", - "cy:open": "node ../../scripts/cypress.js open --e2e --project ${PWD}" - }, - "files": [ - "src" - ], - "types": "src", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/cypress-io/cypress.git" - }, - "homepage": "https://github.com/cypress-io/cypress/tree/develop/npm/xpath#readme", - "author": "Cypress Tools Team", - "bugs": { - "url": "https://github.com/cypress-io/cypress/issues" - }, - "keywords": [ - "cypress", - "cypress-io", - "xpath" - ], - "publishConfig": { - "access": "public" - } -} diff --git a/npm/xpath/src/index.d.ts b/npm/xpath/src/index.d.ts deleted file mode 100644 index 346cc72a4ed5..000000000000 --- a/npm/xpath/src/index.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/// - -declare namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - /** - * Get one or more DOM elements by an XPath selector. - * **Note:** you can test XPath expressions from DevTools console using $x(...) function, for example $x('//div') to find all divs. - * @see https://github.com/cypress-io/cypress-xpath - * @example - * cy.xpath(`//ul[@class="todo-list"]//li`) - * .should('have.length', 3) - */ - xpath(selector: string, options?: Partial): Chainable> - } -} diff --git a/npm/xpath/src/index.js b/npm/xpath/src/index.js deleted file mode 100644 index 62001bebf9a7..000000000000 --- a/npm/xpath/src/index.js +++ /dev/null @@ -1,165 +0,0 @@ -/* eslint-disable no-redeclare */ -/// - -/** - * Adds XPath support to Cypress using a custom command. - * - * @see https://devhints.io/xpath - * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_using_XPath_in_JavaScript - * @example - ```js - it('finds list items', () => { - cy.xpath('//ul[@class="todo-list"]//li') - .should('have.length', 3) - }) - ``` - */ -const xpath = (subject, selector, options = {}) => { - /* global XPathResult */ - const isNumber = (xpathResult) => { - return xpathResult.resultType === XPathResult.NUMBER_TYPE - } - const numberResult = (xpathResult) => xpathResult.numberValue - - const isString = (xpathResult) => { - return xpathResult.resultType === XPathResult.STRING_TYPE - } - const stringResult = (xpathResult) => xpathResult.stringValue - - const isBoolean = (xpathResult) => { - return xpathResult.resultType === XPathResult.BOOLEAN_TYPE - } - const booleanResult = (xpathResult) => xpathResult.booleanValue - - const isPrimitive = (x) => { - return Cypress._.isNumber(x) || Cypress._.isString(x) || Cypress._.isBoolean(x) - } - - // options to log later - const log = { - name: 'xpath', - message: selector, - } - - if (Cypress.dom.isElement(subject) && subject.length > 1) { - throw new Error( - `xpath() can only be called on a single element. Your subject contained ${ - subject.length - } elements.`, - ) - } - - const getValue = () => { - let nodes = [] - let contextNode - let withinSubject = cy.state('withinSubject') - - if (Cypress.dom.isElement(subject)) { - contextNode = subject[0] - } else if (Cypress.dom.isDocument(subject)) { - contextNode = subject - } else if (withinSubject) { - contextNode = withinSubject[0] - } else { - contextNode = cy.state('window').document - } - - let iterator = (contextNode.ownerDocument || contextNode).evaluate( - selector, - contextNode, - ) - - if (isNumber(iterator)) { - const result = numberResult(iterator) - - log.consoleProps = () => { - return { - XPath: selector, - type: 'number', - result, - } - } - - return result - } - - if (isString(iterator)) { - const result = stringResult(iterator) - - log.consoleProps = () => { - return { - XPath: selector, - type: 'string', - result, - } - } - - return result - } - - if (isBoolean(iterator)) { - const result = booleanResult(iterator) - - log.consoleProps = () => { - return { - XPath: selector, - type: 'boolean', - result, - } - } - - return result - } - - try { - let node = iterator.iterateNext() - - while (node) { - nodes.push(node) - node = iterator.iterateNext() - } - - log.consoleProps = () => { - return { - XPath: selector, - result: nodes.length === 1 ? nodes[0] : nodes, - } - } - - return nodes - } catch (e) { - console.error('Document tree modified during iteration', e) - - return null - } - } - - const resolveValue = () => { - return Cypress.Promise.try(getValue).then((value) => { - if (!isPrimitive(value)) { - value = Cypress.$(value) - // Add the ".selector" property because Cypress uses it for error messages - value.selector = selector - } - - return cy.verifyUpcomingAssertions(value, options, { - onRetry: resolveValue, - }) - }) - } - - return resolveValue().then((value) => { - if (options.log !== false) { - // TODO set found elements on the command log? - Cypress.log(log) - } - - return value - }) -} - -Cypress.Commands.add( - 'xpath', - { prevSubject: ['optional', 'element', 'document'] }, - xpath, -) diff --git a/nx.json b/nx.json new file mode 100644 index 000000000000..76786f62c297 --- /dev/null +++ b/nx.json @@ -0,0 +1,11 @@ +{ + "tasksRunnerOptions": { + "default": { + "runner": "nx-cloud", + "options": { + "cacheableOperations": [], + "accessToken": "ZmNlNjA0YzAtNTM1NS00MDIwLWFlMWItNWYxYzNiMjQ4N2VkfHJlYWQtb25seQ==" + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index d78b40a7a434..3275e3d1d654 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cypress", - "version": "12.12.0", + "version": "12.13.0", "description": "Cypress is a next generation front end testing tool built for the modern web", "private": true, "scripts": { @@ -85,6 +85,7 @@ "@graphql-tools/delegate": "8.2.1", "@graphql-tools/utils": "8.2.3", "@graphql-tools/wrap": "8.1.1", + "@nrwl/nx-cloud": "16.0.5", "@octokit/auth-app": "3.6.1", "@octokit/core": "3.6.0", "@percy/cli": "1.2.0", diff --git a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts index d49d3bf8aaba..5e553e0e9ddd 100644 --- a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts +++ b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts @@ -395,4 +395,37 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100 expect(ctx.actions.project.initializeActiveProject).to.be.called }) }) + + describe('runSpec mutation', () => { + it('should trigger expected spec from POST', () => { + startAtSpecsPage('e2e') + + cy.contains('E2E specs').should('be.visible') + + cy.withCtx(async (ctx) => { + const url = `http://127.0.0.1:${ctx.gqlServerPort}/__launchpad/graphql?` + const payload = `{"query":"mutation{\\nrunSpec(specPath:\\"cypress/e2e/dom-content.spec.js\\"){\\n__typename\\n... on RunSpecResponse{\\ntestingType\\nbrowser{\\nid\\nname\\n}\\nspec{\\nid\\nname\\n}\\n}\\n}\\n}","variables":null}` + + ctx.coreData.app.browserStatus = 'open' + + /* + Note: If this test starts failing, this fetch is the likely culprit. + Validate the GQL payload above is still valid by logging the fetch response JSON + */ + + await ctx.util.fetch( + url, + { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: payload, + }, + ) + }) + + cy.contains('Dom Content').should('be.visible') + }) + }) }) diff --git a/packages/app/cypress/e2e/runs.cy.ts b/packages/app/cypress/e2e/runs.cy.ts index cdf45b776561..e2d7e74fd136 100644 --- a/packages/app/cypress/e2e/runs.cy.ts +++ b/packages/app/cypress/e2e/runs.cy.ts @@ -2,6 +2,10 @@ import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json' import type { SinonStub } from 'sinon' function moveToRunsPage (): void { + cy.withCtx((ctx, o) => { + o.sinon.stub(ctx.lifecycleManager.git!, 'currentBranch').value('fakeBranch') + }) + cy.findByTestId('sidebar-link-runs-page').click() cy.findByTestId('app-header-bar').findByText('Runs').should('be.visible') cy.findByTestId('runs-container').should('be.visible') @@ -34,6 +38,9 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.scaffoldProject('component-tests') cy.openProject('component-tests') cy.startAppServer('component') + cy.withCtx((ctx, o) => { + o.sinon.stub(ctx.lifecycleManager.git!, 'currentBranch').value('fakeBranch') + }) }) it('resolves the runs page', () => { @@ -99,7 +106,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { moveToRunsPage() cy.contains('a', 'OVERLIMIT').click() - cy.withCtx((ctx) => { + cy.withCtx((ctx, o) => { expect((ctx.actions.electron.openExternal as SinonStub).lastCall.lastArg).to.contain('http://dummy.cypress.io/runs/4') }) }) @@ -315,6 +322,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { cy.withCtx(async (ctx, o) => { o.sinon.spy(ctx.cloud, 'executeRemoteGraphQL') + o.sinon.stub(ctx.lifecycleManager.git!, 'currentBranch').value('fakeBranch') const config = await ctx.project.getConfig() expect(config.projectId).to.not.equal('newProjectId') @@ -757,6 +765,10 @@ describe('App: Runs', { viewportWidth: 1200 }, () => { }) it('should remove the alert warning if the app reconnects to the internet', () => { + cy.withCtx((ctx, o) => { + o.sinon.stub(ctx.lifecycleManager.git!, 'currentBranch').value('fakeBranch') + }) + cy.loginUser() cy.visitApp() cy.wait(1000) diff --git a/packages/app/cypress/e2e/specs_list_e2e.cy.ts b/packages/app/cypress/e2e/specs_list_e2e.cy.ts index df59656ffd44..a8a98ed481a3 100644 --- a/packages/app/cypress/e2e/specs_list_e2e.cy.ts +++ b/packages/app/cypress/e2e/specs_list_e2e.cy.ts @@ -88,7 +88,9 @@ describe('App: Spec List (E2E)', () => { it('lists files after folders when in same directory', () => { cy.findAllByTestId('row-directory-depth-2').first().click() - cy.get('[id="speclist-cypress/e2e/admin_users/"]') + const rowId = getPathForPlatform('speclist-cypress/e2e/admin_users/').replaceAll('\\', '\\\\') + + cy.get(`[id="${rowId}"]`) .next() .should('contain', 'admin.user') .next() diff --git a/packages/app/package.json b/packages/app/package.json index b0a4cff12435..109d59767634 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -21,7 +21,7 @@ "dependencies": {}, "devDependencies": { "@cypress-design/vue-button": "0.9.2", - "@cypress-design/vue-icon": "0.22.1", + "@cypress-design/vue-icon": "0.22.2", "@cypress-design/vue-statusicon": "0.4.3", "@graphql-typed-document-node/core": "^3.1.0", "@headlessui/vue": "1.4.0", diff --git a/packages/app/src/debug/DebugContainer.cy.tsx b/packages/app/src/debug/DebugContainer.cy.tsx index 9b8f30362d8c..d19613cde2f8 100644 --- a/packages/app/src/debug/DebugContainer.cy.tsx +++ b/packages/app/src/debug/DebugContainer.cy.tsx @@ -46,6 +46,7 @@ describe('', () => { const userProjectStatusStore = useUserProjectStatusStore() userProjectStatusStore.setHasInitiallyLoaded() + userProjectStatusStore.setProjectFlag('isUsingGit', true) validateEmptyState([defaultMessages.debugPage.emptyStates.connectToCypressCloud, defaultMessages.debugPage.emptyStates.connect.title, defaultMessages.debugPage.emptyStates.connect.description]) cy.findByRole('button', { name: 'Connect to Cypress Cloud' }).should('be.visible') @@ -56,6 +57,7 @@ describe('', () => { userProjectStatusStore.setUserFlag('isLoggedIn', true) userProjectStatusStore.setProjectFlag('isProjectConnected', false) + userProjectStatusStore.setProjectFlag('isUsingGit', true) userProjectStatusStore.setHasInitiallyLoaded() validateEmptyState([defaultMessages.debugPage.emptyStates.connect.title, defaultMessages.debugPage.emptyStates.connect.description]) @@ -67,6 +69,7 @@ describe('', () => { userProjectStatusStore.setUserFlag('isLoggedIn', true) userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setProjectFlag('isUsingGit', true) userProjectStatusStore.setHasInitiallyLoaded() cy.mountFragment(DebugSpecsFragmentDoc, { variableTypes: DebugSpecVariableTypes, @@ -78,20 +81,43 @@ describe('', () => { cy.findByDisplayValue('npx cypress run --record --key 2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa').should('be.visible') }) - it('errors', () => { + it('is not using git', () => { const userProjectStatusStore = useUserProjectStatusStore() userProjectStatusStore.setUserFlag('isLoggedIn', true) userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setProjectFlag('isUsingGit', false) userProjectStatusStore.setHasInitiallyLoaded() cy.mountFragment(DebugSpecsFragmentDoc, { variableTypes: DebugSpecVariableTypes, variables: defaultVariables, - render: (gqlVal) => , + render: (gqlVal) => , + }) + + cy.findByTestId('debug-empty-title').should('contain.text', 'Git repository not detected') + }) + + it('has no runs for the current branch', () => { + const { setUserFlag, setProjectFlag, cloudStatusMatches, setHasInitiallyLoaded } = useUserProjectStatusStore() + + setUserFlag('isLoggedIn', true) + setUserFlag('isMemberOfOrganization', true) + setProjectFlag('isProjectConnected', true) + setProjectFlag('hasNoRecordedRuns', true) + setProjectFlag('hasNonExampleSpec', true) + setProjectFlag('isConfigLoaded', true) + setProjectFlag('isUsingGit', true) + setHasInitiallyLoaded() + + cy.mountFragment(DebugSpecsFragmentDoc, { + variableTypes: DebugSpecVariableTypes, + variables: defaultVariables, + render: (gqlVal) => , }) - cy.findByTestId('debug-empty').should('not.exist') - cy.findByTestId('debug-alert').should('be.visible') + expect(cloudStatusMatches('needsRecordedRun')).equals(true) + + cy.contains('No runs found for your branch') }) }) @@ -101,6 +127,7 @@ describe('', () => { userProjectStatusStore.setUserFlag('isLoggedIn', true) userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setProjectFlag('isUsingGit', true) userProjectStatusStore.setHasInitiallyLoaded() }) @@ -228,6 +255,7 @@ describe('', () => { userProjectStatusStore.setUserFlag('isLoggedIn', true) userProjectStatusStore.setProjectFlag('isProjectConnected', true) + userProjectStatusStore.setProjectFlag('isUsingGit', true) userProjectStatusStore.setHasInitiallyLoaded() }) diff --git a/packages/app/src/debug/DebugContainer.vue b/packages/app/src/debug/DebugContainer.vue index 94072128c7a7..7c82b4ce64c4 100644 --- a/packages/app/src/debug/DebugContainer.vue +++ b/packages/app/src/debug/DebugContainer.vue @@ -5,9 +5,7 @@ {{ t('launchpadErrors.noInternet.connectProject') }} - + + + { return props.gql?.currentProject?.cloudProject?.__typename === 'CloudProject' ? props.gql.currentProject.cloudProject diff --git a/packages/app/src/debug/DebugResults.vue b/packages/app/src/debug/DebugResults.vue index 8a2998d82d6d..0d4b2ea042c3 100644 --- a/packages/app/src/debug/DebugResults.vue +++ b/packages/app/src/debug/DebugResults.vue @@ -1,6 +1,6 @@