diff --git a/.gitignore b/.gitignore
index 09048d2..5713cd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,7 @@ dist
# when working with contributors
package-lock.json
yarn.lock
+
+# cypress recordings during a test run are temporary files
+/cypress/videos/
+/cypress/screenshots/
diff --git a/cypress.json b/cypress.json
new file mode 100644
index 0000000..11a38d4
--- /dev/null
+++ b/cypress.json
@@ -0,0 +1,5 @@
+{
+ "baseUrl": "http://localhost:13370",
+ "videoRecording": false,
+ "screenshotOnHeadlessFailure": false
+}
diff --git a/cypress/fixtures/test-app/index.html b/cypress/fixtures/test-app/index.html
new file mode 100644
index 0000000..fa3450e
--- /dev/null
+++ b/cypress/fixtures/test-app/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+ cypress-testing-library
+
+
+
+
+ No auto-reload after changing this static HTML markup:
+ click ↻ Run All Tests.
+
+
+
+ getByText
+
+
+
+
+ getByAltText
+
+
+
+ getByTestId
+
+
+
+
+
+
diff --git a/cypress/integration/commands.spec.js b/cypress/integration/commands.spec.js
new file mode 100644
index 0000000..7f18b34
--- /dev/null
+++ b/cypress/integration/commands.spec.js
@@ -0,0 +1,38 @@
+describe('dom-testing-library commands', () => {
+ beforeEach(() => {
+ cy.visit('/')
+ })
+ it('getByPlaceholderText', () => {
+ cy
+ .getByPlaceholderText('Placeholder Text')
+ .click()
+ .type('Hello Placeholder')
+ })
+
+ it('getByText', () => {
+ cy
+ .getByText('Button Text')
+ .click()
+ })
+
+ it('getByLabelText', () => {
+ cy
+ .getByLabelText('Label For Input Labelled By Id')
+ .click()
+ .type('Hello Input Labelled By Id')
+ })
+
+ it('getByAltText', () => {
+ cy
+ .getByAltText('Image Alt Text')
+ .click()
+ })
+
+ it('getByTestId', () => {
+ cy
+ .getByTestId('image-with-random-alt-tag')
+ .click()
+ })
+})
+
+/* global cy */
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
new file mode 100644
index 0000000..07dd7dd
--- /dev/null
+++ b/cypress/plugins/index.js
@@ -0,0 +1,2 @@
+// Keeping this file here, otherwise it gets recreated by Cypress on each run.
+module.exports = () => {}
diff --git a/cypress/support/index.js b/cypress/support/index.js
new file mode 100644
index 0000000..b2c012d
--- /dev/null
+++ b/cypress/support/index.js
@@ -0,0 +1 @@
+import '../../src/add-commands'
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000..4160d66
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,5 @@
+const jestConfig = require('kcd-scripts/jest')
+
+module.exports = Object.assign(jestConfig, {
+ testEnvironment: 'jest-environment-jsdom',
+})
diff --git a/package.json b/package.json
index 5b96b98..d6f821d 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,14 @@
"add-contributor": "kcd-scripts contributors add",
"build": "kcd-scripts build",
"lint": "kcd-scripts lint",
- "test": "echo TODO",
- "test:ci": "echo TODO",
- "test:update": "npm test -- --updateSnapshot --coverage",
+ "test": "npm-run-all --parallel test:unit test:cypress",
+ "test:unit": "kcd-scripts test --no-watch",
+ "test:unit:watch": "kcd-scripts test",
+ "test:cypress:serve": "serve --clipless --local --port 13370 ./cypress/fixtures/test-app",
+ "test:cypress:run": "cypress run",
+ "test:cypress:open": "cypress open",
+ "test:cypress": "npm-run-all --silent --parallel --race test:cypress:serve test:cypress:run",
+ "test:cypress:dev": "npm-run-all --silent --parallel --race test:cypress:serve test:cypress:open",
"validate": "kcd-scripts validate build,lint,test",
"setup": "npm install && npm run validate -s",
"precommit": "kcd-scripts precommit"
@@ -34,16 +39,23 @@
"author": "Kent C. Dodds (http://kentcdodds.com/)",
"license": "MIT",
"dependencies": {
- "dom-testing-library": "^1.0.0"
+ "dom-testing-library": "^1.3.0"
},
"devDependencies": {
- "kcd-scripts": "^0.36.1"
+ "cypress": "^2.1.0",
+ "kcd-scripts": "^0.37.0",
+ "npm-run-all": "^4.1.2",
+ "serve": "^6.5.4"
},
"peerDependencies": {
"cypress": "^2.1.0"
},
"eslintConfig": {
- "extends": "./node_modules/kcd-scripts/eslint.js"
+ "extends": "./node_modules/kcd-scripts/eslint.js",
+ "rules": {
+ "import/prefer-default-export": "off",
+ "import/no-unassigned-import": "off"
+ }
},
"eslintIgnore": [
"node_modules",
diff --git a/src/__tests__/__snapshots__/commands.js.snap b/src/__tests__/__snapshots__/commands.js.snap
new file mode 100644
index 0000000..c9dd4f1
--- /dev/null
+++ b/src/__tests__/__snapshots__/commands.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`exports expected commands 1`] = `
+Array [
+ "getByPlaceholderText",
+ "getByText",
+ "getByLabelText",
+ "getByAltText",
+ "getByTestId",
+]
+`;
diff --git a/src/__tests__/add-commands.js b/src/__tests__/add-commands.js
new file mode 100644
index 0000000..e754820
--- /dev/null
+++ b/src/__tests__/add-commands.js
@@ -0,0 +1,19 @@
+import {commands} from '../'
+
+test('adds commands to Cypress', () => {
+ const addMock = jest.fn().mockName('Cypress.Commands.add')
+ global.Cypress = {Commands: {add: addMock}}
+ global.cy = {}
+
+ require('../add-commands')
+
+ expect(addMock).toHaveBeenCalledTimes(commands.length)
+ commands.forEach(({name}, index) => {
+ expect(addMock.mock.calls[index]).toMatchObject([
+ name,
+ // We get a new function that is `command.bind(null, cy)` i.e. global `cy` passed into the first argument.
+ // The commands themselves will be tested separately in the Cypress end-to-end tests.
+ expect.any(Function),
+ ])
+ })
+})
diff --git a/src/__tests__/commands.js b/src/__tests__/commands.js
new file mode 100644
index 0000000..5e88f83
--- /dev/null
+++ b/src/__tests__/commands.js
@@ -0,0 +1,14 @@
+import {commands} from '../'
+
+test('exports expected commands', () => {
+ expect(commands).toMatchObject(expect.any(Array))
+ expect(commands.map(({name}) => name)).toMatchSnapshot()
+ commands.forEach(command =>
+ expect(command).toMatchObject({
+ name: expect.any(String),
+ // We get a new function that wraps the respective query from `dom-testing-library`.
+ // The commands themselves will be tested separately in the Cypress end-to-end tests.
+ command: expect.any(Function),
+ }),
+ )
+})
diff --git a/src/add-commands.js b/src/add-commands.js
index 43e8f14..52d90ba 100644
--- a/src/add-commands.js
+++ b/src/add-commands.js
@@ -1,7 +1,7 @@
import {commands} from './'
commands.forEach(({name, command}) => {
- Cypress.Commands.add(name, command)
+ Cypress.Commands.add(name, command.bind(null, cy))
})
-/* global Cypress */
+/* global Cypress, cy */
diff --git a/src/index.js b/src/index.js
index a18dff4..97528bd 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,44 +1,27 @@
-import {queries} from 'dom-testing-library'
+import {queries, waitForElement} from 'dom-testing-library'
const commands = Object.keys(queries)
.filter(queryName => queryName.startsWith('getBy'))
.map(queryName => {
return {
name: queryName,
- command: (...args) => {
- const fn = new Function(
- 'args',
- 'query',
- 'getCommandWaiter',
+ command: (cy, ...args) => {
+ const queryImpl = queries[queryName]
+ const commandImpl = doc =>
+ waitForElement(() => queryImpl(doc, ...args), {container: doc})
+ const thenHandler = new Function(
+ 'commandImpl',
`
- return function Command__${queryName}({document}) {
- return getCommandWaiter(document, () => query(document, ...args))();
- };
+ return function Command__${queryName}(thenArgs) {
+ return commandImpl(thenArgs.document)
+ }
`,
- )(args, queries[queryName], getCommandWaiter)
- return cy.window({log: false}).then(fn)
+ )(commandImpl)
+ return cy.window({log: false}).then(thenHandler)
},
}
})
-function getCommandWaiter(container, fn) {
- return function waiter() {
- const val = fn()
- if (val) {
- return val
- } else {
- return new Promise(resolve => {
- const observer = new MutationObserver(() => {
- observer.disconnect()
- resolve(waiter())
- })
- observer.observe(container, {subtree: true, childList: true})
- })
- }
- }
-}
-
-export {commands, getCommandWaiter}
+export {commands}
-/* eslint no-new-func:0, import/default:0 */
-/* global cy */
+/* eslint no-new-func:0 */