Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create from React component #25168

Merged
merged 16 commits into from Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions .circleci/workflows.yml
Expand Up @@ -28,7 +28,7 @@ mainBuildFilters: &mainBuildFilters
only:
- develop
- /^release\/\d+\.\d+\.\d+$/
- 'ryanm/fix/column-line'
- 'feature/create-from-react-component'

# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
Expand All @@ -37,15 +37,15 @@ macWorkflowFilters: &darwin-workflow-filters
when:
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ 'ryanm/fix/column-line', << pipeline.git.branch >> ]
- equal: [ 'feature/create-from-react-component', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
when:
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ 'ryanm/fix/column-line', << pipeline.git.branch >> ]
- equal: [ 'feature/create-from-react-component', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -63,7 +63,7 @@ windowsWorkflowFilters: &windows-workflow-filters
when:
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ 'ryanm/fix/column-line', << pipeline.git.branch >> ]
- equal: [ 'feature/create-from-react-component', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand Down Expand Up @@ -129,7 +129,7 @@ commands:
- run:
name: Check current branch to persist artifacts
command: |
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "ryanm/fix/column-line" ]]; then
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feature/create-from-react-component" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
Expand Down
2 changes: 2 additions & 0 deletions npm/webpack-dev-server/cypress/e2e/react.cy.ts
Expand Up @@ -129,6 +129,8 @@ for (const project of WEBPACK_REACT) {

// 4. recreate spec, with same name as removed spec
cy.findByTestId('new-spec-button').click()
cy.findByRole('button', { name: 'Create new empty spec' }).should('be.visible').click()

cy.findByRole('dialog').within(() => {
cy.get('input').clear().type('src/App.cy.jsx')
cy.contains('button', 'Create spec').click()
Expand Down
2 changes: 1 addition & 1 deletion npm/webpack-preprocessor/__snapshots__/compilation.spec.js
@@ -1,6 +1,6 @@
exports['webpack preprocessor - e2e correctly preprocesses the file 1'] = `
it("is a test",(function(){expect(1).to.equal(1),expect(2).to.equal(2),expect(Math.min.apply(Math,[3,4])).to.equal(3)}));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZV9zcGVjX291dHB1dC5qcyIsIm1hcHBpbmdzIjoiQUFBQUEsR0FBRyxhQUFhLFdBR2RDLE9BRmdCLEdBRU5DLEdBQUdDLE1BQU0sR0FDbkJGLE9BSG1CLEdBR1RDLEdBQUdDLE1BQU0sR0FDbkJGLE9BQU9HLEtBQUtDLElBQUwsTUFBQUQsS0FBWSxDQUFDLEVBQUcsS0FBS0YsR0FBR0MsTUFBTSIsInNvdXJjZXMiOlsid2VicGFjazovL0BjeXByZXNzL3dlYnBhY2stcHJlcHJvY2Vzc29yLy4vdGVzdC9fdGVzdC1vdXRwdXQvZXhhbXBsZV9zcGVjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIml0KCdpcyBhIHRlc3QnLCAoKSA9PiB7XG4gIGNvbnN0IFthLCBiXSA9IFsxLCAyXVxuXG4gIGV4cGVjdChhKS50by5lcXVhbCgxKVxuICBleHBlY3QoYikudG8uZXF1YWwoMilcbiAgZXhwZWN0KE1hdGgubWluKC4uLlszLCA0XSkpLnRvLmVxdWFsKDMpXG59KVxuIl0sIm5hbWVzIjpbIml0IiwiZXhwZWN0IiwidG8iLCJlcXVhbCIsIk1hdGgiLCJtaW4iXSwic291cmNlUm9vdCI6IiJ9
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZV9zcGVjX291dHB1dC5qcyIsIm1hcHBpbmdzIjoiQUFBQUEsR0FBRyxhQUFhLFdBR2RDLE9BRmdCLEdBRU5DLEdBQUdDLE1BQU0sR0FDbkJGLE9BSG1CLEdBR1RDLEdBQUdDLE1BQU0sR0FDbkJGLE9BQU9HLEtBQUtDLElBQUcsTUFBUkQsS0FBWSxDQUFDLEVBQUcsS0FBS0YsR0FBR0MsTUFBTSIsInNvdXJjZXMiOlsid2VicGFjazovL0BjeXByZXNzL3dlYnBhY2stcHJlcHJvY2Vzc29yLy4vdGVzdC9fdGVzdC1vdXRwdXQvZXhhbXBsZV9zcGVjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIml0KCdpcyBhIHRlc3QnLCAoKSA9PiB7XG4gIGNvbnN0IFthLCBiXSA9IFsxLCAyXVxuXG4gIGV4cGVjdChhKS50by5lcXVhbCgxKVxuICBleHBlY3QoYikudG8uZXF1YWwoMilcbiAgZXhwZWN0KE1hdGgubWluKC4uLlszLCA0XSkpLnRvLmVxdWFsKDMpXG59KVxuIl0sIm5hbWVzIjpbIml0IiwiZXhwZWN0IiwidG8iLCJlcXVhbCIsIk1hdGgiLCJtaW4iXSwic291cmNlUm9vdCI6IiJ9
`

exports['webpack preprocessor - e2e has less verbose syntax error 1'] = `
Expand Down
Expand Up @@ -220,7 +220,6 @@ describe('./lib/cross-origin-callback-loader', () => {
expectAddFileSource(store).to.equal(stripIndent`
__cypressCrossOriginCallback = () => {
const utils = require('../support/utils');

utils.foo();
}`)
})
Expand All @@ -230,9 +229,7 @@ describe('./lib/cross-origin-callback-loader', () => {
`it('test', () => {
cy.origin('http://www.foobar.com:3500', () => {
require('../support/commands')

const utils = require('../support/utils')

const _ = require('lodash')
})
})`,
Expand All @@ -241,9 +238,7 @@ describe('./lib/cross-origin-callback-loader', () => {
expectAddFileSource(store).to.equal(stripIndent`
__cypressCrossOriginCallback = () => {
require('../support/commands');

const utils = require('../support/utils');

const _ = require('lodash');
}`)
})
Expand All @@ -270,9 +265,7 @@ describe('./lib/cross-origin-callback-loader', () => {
`it('test', () => {
cy.origin('http://www.foobar.com:3500', () => {
const someVar = 'someValue'

const result = require('./fn')(someVar)

expect(result).to.equal('mutated someVar')
})
})`,
Expand All @@ -281,9 +274,7 @@ describe('./lib/cross-origin-callback-loader', () => {
expectAddFileSource(store).to.equal(stripIndent`
__cypressCrossOriginCallback = () => {
const someVar = 'someValue';

const result = require('./fn')(someVar);

expect(result).to.equal('mutated someVar');
}`)
})
Expand All @@ -293,7 +284,6 @@ describe('./lib/cross-origin-callback-loader', () => {
`it('test', () => {
cy.origin('http://www.foobar.com:3500', { args: { foo: 'foo'}}, ({ foo }) => {
const result = require('./fn')(foo)

expect(result).to.equal('mutated someVar')
})
})`,
Expand All @@ -304,7 +294,6 @@ describe('./lib/cross-origin-callback-loader', () => {
foo
}) => {
const result = require('./fn')(foo);

expect(result).to.equal('mutated someVar');
}`)
})
Expand Down
192 changes: 168 additions & 24 deletions packages/app/cypress/e2e/create-from-component.cy.ts
@@ -1,10 +1,10 @@
import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json'
import { getPathForPlatform } from '../../src/paths'

function validateCreateFromComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
function validateCreateFromVueComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
beforeEach(beforeEachFn)

it('Shows create from component card for Vue projects with default spec patterns', () => {
it('Shows create from component card for Vue projects', () => {
cy.get('@ComponentCard')
.within(() => {
cy.findByRole('button', {
Expand Down Expand Up @@ -82,31 +82,175 @@ function validateCreateFromComponentCard (beforeEachFn: () => void, expectedSpec
.should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`).click()
})

cy.findByText('<HelloWorld ... />', { timeout: 10000 }).should('be.visible')
cy.waitForSpecToFinish({ passCount: 1 })
})
}

function validateCreateFromReactComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
beforeEach(beforeEachFn)

it('Shows create from component card for React projects', () => {
cy.get('@ComponentCard')
.within(() => {
cy.findByRole('button', {
name: 'Create from component',
}).should('be.visible')
.and('not.be.disabled')
})
})

it('Can be closed with the x button', () => {
cy.get('@ComponentCard').click()

cy.findByRole('button', { name: 'Close' }).as('DialogCloseButton')

cy.get('@DialogCloseButton').click()
cy.findByRole('dialog', {
name: 'Choose a component',
}).should('not.exist')
})

it('Lists files in the project', () => {
cy.get('@ComponentCard').click()

cy.findByText('5 matches').should('be.visible')

cy.findByText('App').should('be.visible')
cy.findByText('index').should('be.visible')
})

it('Allows for the user to search through their components', () => {
cy.get('@ComponentCard').click()

cy.findByText('*.{js,jsx,tsx}').should('be.visible')
cy.findByText('5 matches').should('be.visible')
cy.findByLabelText('file-name-input').type('App')

cy.findByText('App').should('be.visible')
cy.findByText('1 of 5 matches').should('be.visible')
cy.findByText('index').should('not.exist')
cy.findByText('component').should('not.exist')
})

it('shows \'No components found\' if there are no exported components', () => {
cy.get('@ComponentCard').click()

cy.findByText('index').should('be.visible').click()

cy.findByTestId('react-component-row').should('not.exist')
cy.contains('No components found').should('be.visible')
})

it('shows \'Unable to parse file\' if there was an error parsing the file', () => {
cy.get('@ComponentCard').click()

// This component has a syntax error so we will fail to parse it
cy.findByText('Invalid').should('be.visible').click()

cy.findByTestId('react-component-row').should('not.exist')
cy.contains('Unable to parse file').should('be.visible')
})

it('shows success modal when component spec is created', () => {
cy.get('@ComponentCard').click()

// Expand the row
cy.findByText('App').should('be.visible').click()

// Click on 'app' component
cy.findByTestId('react-component-row').should('contain', 'App').click()

cy.findByRole('dialog', {
name: defaultMessages.createSpec.successPage.header,
}).as('SuccessDialog').within(() => {
cy.contains(getPathForPlatform(expectedSpecPath)).should('be.visible')
cy.findByRole('button', { name: 'Close' }).should('be.visible')

cy.findByRole('link', { name: 'Okay, run the spec' })
.should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`)

cy.findByRole('button', { name: 'Create another spec' }).click()
})

// 'Create from component' card appears again when the user selects "create another spec"
cy.findByText('Create from component').should('be.visible')
})

it('runs generated spec', () => {
cy.get('@ComponentCard').click()

// Expand the row
cy.findByText('App').should('be.visible').click()

// Click on 'app' component
cy.findByTestId('react-component-row').should('contain', 'App').click()

cy.findByRole('dialog', {
name: defaultMessages.createSpec.successPage.header,
}).as('SuccessDialog').within(() => {
cy.contains(getPathForPlatform(expectedSpecPath)).should('be.visible')
cy.findByRole('button', { name: 'Close' }).should('be.visible')

// There appears to be a race condition here where sometimes we try to run the spec
// before the file has been written to. Waiting here for 1 second resolves the issue.
cy.wait(2000)

cy.findByRole('link', { name: 'Okay, run the spec' })
.should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`).click()
})

cy.waitForSpecToFinish({ passCount: 1 })
})
}

describe('Create from component card', () => {
context('project with default spec pattern', () => {
validateCreateFromComponentCard(() => {
cy.scaffoldProject('no-specs-vue-2')
cy.openProject('no-specs-vue-2')
cy.startAppServer('component')
cy.visitApp()

cy.findAllByTestId('card').eq(0).as('ComponentCard')
}, 'src/components/HelloWorld.cy.js')
})

context('project with custom spec pattern', () => {
validateCreateFromComponentCard(() => {
cy.scaffoldProject('no-specs-vue-2')
cy.openProject('no-specs-vue-2', ['--config-file', 'cypress-custom-spec-pattern.config.js'])
cy.startAppServer('component')
cy.visitApp()

cy.findByText('New spec').click()
cy.findAllByTestId('card').eq(0).as('ComponentCard')
}, 'src/specs-folder/HelloWorld.cy.js')
context('Vue', () => {
context('project with default spec pattern', () => {
validateCreateFromVueComponentCard(() => {
cy.scaffoldProject('no-specs-vue-2')
cy.openProject('no-specs-vue-2')
cy.startAppServer('component')
cy.visitApp()

cy.findAllByTestId('card').eq(0).as('ComponentCard')
}, 'src/components/HelloWorld.cy.js')
})

context('project with custom spec pattern', () => {
validateCreateFromVueComponentCard(() => {
cy.scaffoldProject('no-specs-vue-2')
cy.openProject('no-specs-vue-2', ['--config-file', 'cypress-custom-spec-pattern.config.js'])
cy.startAppServer('component')
cy.visitApp()

cy.findByText('New spec').click()
cy.findAllByTestId('card').eq(0).as('ComponentCard')
}, 'src/specs-folder/HelloWorld.cy.js')
})
})

context('React', () => {
context('project with default spec pattern', () => {
validateCreateFromReactComponentCard(() => {
cy.scaffoldProject('no-specs')
cy.openProject('no-specs')
cy.startAppServer('component')
cy.visitApp()

cy.findAllByTestId('card').eq(0).as('ComponentCard')
}, 'src/App.cy.jsx')
})

context('project with custom spec pattern', () => {
validateCreateFromReactComponentCard(() => {
cy.scaffoldProject('no-specs')
cy.openProject('no-specs', ['--config-file', 'cypress-custom-spec-pattern.config.ts'])
cy.startAppServer('component')
cy.visitApp()

cy.findByText('New spec').click()
cy.findAllByTestId('card').eq(0).as('ComponentCard')
}, 'src/specs-folder/App.cy.jsx')
})
})
})