diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml
index 5e0ac59159..a1ce01556f 100644
--- a/.circleci/workflows.yml
+++ b/.circleci/workflows.yml
@@ -1944,20 +1944,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
diff --git a/.gitignore b/.gitignore
index 65d1854e60..f67b890a11 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 0fe28547aa..becc1f915c 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 e73e5d6a93..09763aa540 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/npm/xpath/.eslintrc b/npm/xpath/.eslintrc
deleted file mode 100644
index 2979c0d4f3..0000000000
--- 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 17d3bb8714..0000000000
--- 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 36e869b7cb..0000000000
--- 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 339fce4e35..de39c50328 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 e7a2ffb6dc..0000000000
--- 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 ebe8a49935..0000000000
--- 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 c88881f93a..0000000000
--- 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 da18d9352a..0000000000
--- 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 10ea61f87d..0000000000
--- 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 97ebfa8f41..0000000000
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 c4b72bb9ea..0000000000
--- 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 346cc72a4e..0000000000
--- 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 62001bebf9..0000000000
--- 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,
-)