From 7bd0a328f6a0b324550b7d82013b8d4d240f2cfb Mon Sep 17 00:00:00 2001 From: Nicholas Boll Date: Wed, 11 Mar 2020 23:44:27 -0600 Subject: [PATCH 1/2] chore: remove deprecated `query*` queries and prevSubject fallback code BREAKING CHANGE: Remove `query*` queries. Throw an error instead. Fixing requires updating all `query*` to `find*` queries. In practice this means replacing `cy.query` with `cy.find` BREAKING CHANGE: Remove fallback that retries chaiined query that assumes no previous subject. In practice this means starting new chains if no previous subject is required. ```js // Before cy.findByText('Foo') .click() .findByText('Bar') // Element with 'Bar' text is not a child of an element with 'Foo' text .click() // After cy.findByText('Foo') .click() cy.findByText('Bar') .click() ``` --- cypress/integration/configure.spec.js | 18 --- cypress/integration/find.spec.js | 22 +-- cypress/integration/query.spec.js | 203 ++++---------------------- src/index.js | 49 +------ 4 files changed, 36 insertions(+), 256 deletions(-) delete mode 100644 cypress/integration/configure.spec.js diff --git a/cypress/integration/configure.spec.js b/cypress/integration/configure.spec.js deleted file mode 100644 index beb113e..0000000 --- a/cypress/integration/configure.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -/// -describe('configuring fallback globally', () => { - beforeEach(() => { - cy.visit('cypress/fixtures/test-app/') - cy.configureCypressTestingLibrary({ fallbackRetryWithoutPreviousSubject: false }) - }) - - it('findByText with a previous subject', () => { - cy.get('#nested') - .findByText('Button Text 1') - .should('not.exist') - cy.get('#nested') - .findByText('Button Text 2') - .should('exist') - }) -}) - -/* global cy */ diff --git a/cypress/integration/find.spec.js b/cypress/integration/find.spec.js index 96fc2a0..d69443b 100644 --- a/cypress/integration/find.spec.js +++ b/cypress/integration/find.spec.js @@ -108,7 +108,7 @@ describe('find* dom-testing-library commands', () => { it('findByText with a previous subject', () => { cy.get('#nested') - .findByText('Button Text 1', {fallbackRetryWithoutPreviousSubject: false}) + .findByText('Button Text 1') .should('not.exist') cy.get('#nested') .findByText('Button Text 2') @@ -188,15 +188,6 @@ describe('find* dom-testing-library commands', () => { cy.findByText(/^Button Text/i, {timeout: 100}) }) - it('findByText should not break existing code', () => { - cy.window() - .findByText('Button Text 1') - .should('exist') - cy.location() - .findByText('Button Text 1') - .should('exist') - }) - it('findByText should show as a parent command if it starts a chain', () => { const assertLog = (attrs, log) => { if (log.get('name') === 'findByText') { @@ -218,17 +209,6 @@ describe('find* dom-testing-library commands', () => { cy.on('log:added', assertLog) cy.get('body').findByText('Button Text 1') }) - - it('should chain findBy* with subject different of document, element or window', () => { - cy.wrap(true) - .should('be.true') - .findByText('Error message') - .findByLabelText(/Required/i) - .type('something') - .findByText('Submit') - .queryByText('Error message') - .should('not.be.visible') - }) }) /* global cy */ diff --git a/cypress/integration/query.spec.js b/cypress/integration/query.spec.js index 3ef0905..bb5e7b0 100644 --- a/cypress/integration/query.spec.js +++ b/cypress/integration/query.spec.js @@ -1,190 +1,43 @@ -/// -describe('query* dom-testing-library commands', () => { +describe('get* queries should error', () => { beforeEach(() => { - cy.visit('cypress/fixtures/test-app/') + cy.visit('cypress/fixtures/test-app/') }) - // Test each of the types of queries: LabelText, PlaceholderText, Text, DisplayValue, AltText, Title, Role, TestId + const queryPrefixes = ['By', 'AllBy'] + const queryTypes = ['LabelText', 'PlaceholderText', 'Text', 'DisplayValue', 'AltText', 'Title', 'Role', 'TestId'] - it('queryByLabelText', () => { - cy.queryByLabelText('Label 1') - .click() - .type('Hello Input Labelled By Id') - }) - - it('queryAllByLabelText', () => { - cy.queryAllByLabelText(/^Label \d$/).should('have.length', 2) - }) - - it('queryByPlaceholderText', () => { - cy.queryByPlaceholderText('Input 1') - .click() - .type('Hello Placeholder') - }) + queryPrefixes.forEach(queryPrefix => { + queryTypes.forEach(queryType => { + const obsoleteQueryName = `query${queryPrefix + queryType}`; + const preferredQueryName = `find${queryPrefix + queryType}`; + it(`${obsoleteQueryName} should error and suggest using ${preferredQueryName}`, () => { - it('queryAllByPlaceholderText', () => { - cy.queryAllByPlaceholderText(/^Input \d$/).should('have.length', 2) - }) + const errorMessage = `You used '${obsoleteQueryName}' which has been removed from Cypress Testing Library because it does not make sense in this context. Please use '${preferredQueryName}' instead.` + cy.on('fail', err => { + expect(err.message).to.eq(errorMessage) + }) - it('queryByText', () => { - cy.queryByText('Button Text 1') - .click() - .should('contain', 'Button Clicked') - }) + cy[`${obsoleteQueryName}`]('Irrelevant') + }) - it('queryAllByText', () => { - cy.queryAllByText(/^Button Text \d$/) - .should('have.length', 2) - .click({ multiple: true }) - .should('contain', 'Button Clicked') - }) + it(`${obsoleteQueryName} should not log more than once`, () => { - it('queryByDisplayValue', () => { - cy.queryByDisplayValue('Display Value 1') - .click() - .clear() - .type('Some new text') - }) - - it('queryAllByDisplayValue', () => { - cy.queryAllByDisplayValue(/^Display Value \d$/) - .should('have.length', 2) - }) - - it('queryByAltText', () => { - cy.queryByAltText('Image Alt Text 1').click() - }) - - it('queryAllByAltText', () => { - cy.queryAllByAltText(/^Image Alt Text \d$/).should('have.length', 2) - }) + let logCount = 0 + cy.on('log:added', (attrs, log) => { + if (log.get('name') === obsoleteQueryName) { + logCount = logCount + 1 + } + }) - it('queryByTitle', () => { - cy.queryByTitle('Title 1').click() - }) - - it('queryAllByTitle', () => { - cy.queryAllByTitle(/^Title \d$/).should('have.length', 2) - }) - - it('queryByRole', () => { - cy.queryByRole('dialog').click() - }) - - it('queryAllByRole', () => { - cy.queryAllByRole(/^dialog/).should('have.length', 2) - }) - - it('queryByTestId', () => { - cy.queryByTestId('image-with-random-alt-tag-1').click() - }) - - it('queryAllByTestId', () => { - cy.queryAllByTestId(/^image-with-random-alt-tag-\d$/).should('have.length', 2) - }) - - /* Test the behaviour around these queries */ - - it('queryByText with .should(\'not.exist\')', () => { - cy.queryAllByText(/^Button Text \d$/).should('exist') - cy.queryByText('Non-existing Button Text', {timeout: 100}).should('not.exist') - }) + cy.on('fail', _ => { + expect(logCount).to.equal(1) + cy.removeAllListeners('log:added') + }) - it('queryByText within', () => { - cy.get('#nested').within(() => { - cy.queryByText('Button Text 2').click() - }) - }) - - it('queryByText should set the Cypress element to the found element', (done) => { - // This test is a little strange since snapshots show what element - // is selected, but snapshots themselves don't give access to those - // elements. I had to make the implementation specific so that the `$el` - // is the `subject` when the log is added and the `$el` is the `value` - // when the log is changed. It would be better to extract the `$el` from - // each snapshot - - cy.on('log:changed', (attrs, log) => { - if (log.get('name') === 'queryByText') { - expect(log.get('$el')).to.have.text('Button Text 1') - done() - } - }) - - cy.queryByText('Button Text 1') - }) - - it('query* will return immediately, and never retry', () => { - cy.queryByText('Next Page').click() - - const errorMessage = `Unable to find an element with the text: New Page Loaded.` - cy.on('fail', err => { - expect(err.message).to.contain(errorMessage) - }) - - cy.queryByText('New Page Loaded', { timeout: 300 }).should('exist') - }) - - it('query* in container', () => { - return cy.get('#nested') - .then(subject => { - cy.queryByText(/^Button Text/, {container: subject}).click() + cy[`${obsoleteQueryName}`]('Irrelevant') + }) }) }) - - it('queryByText can return no result, and should not error', () => { - const text = 'Supercalifragilistic' - - cy.queryByText(text, {timeout: 100}) - .should('have.length', 0) - .and('not.exist') - }) - - it('queryAllByText can return no results message should not error', () => { - const text = 'Supercalifragilistic' - - cy.queryAllByText(text, {timeout: 100}) - .should('have.length', 0) - .and('not.exist') - }) - - it('queryAllByText should forward existence error message from @testing-library/dom', () => { - const text = 'Supercalifragilistic' - const errorMessage = `Unable to find an element with the text: Supercalifragilistic.` - cy.on('fail', err => { - expect(err.message).to.contain(errorMessage) - }) - - cy.queryAllByText(text, {timeout: 100}).should('exist') - }) - - it('queryByLabelText should forward useful error messages from @testing-library/dom', () => { - const errorMessage = `Found a label with the text of: Label 3, however no form control was found associated to that label.` - cy.on('fail', err => { - expect(err.message).to.contain(errorMessage) - }) - - cy.queryByLabelText('Label 3', {timeout: 100}).should('exist') - }) - - it('queryAllByText should default to Cypress non-existence error message', () => { - const errorMessage = `Expected