Skip to content

Commit f61688c

Browse files
astone123ryanthemanuellmiller1990
authored
feat: create from React component UI (#24982)
* feat: WIP server logic for create from React component * feat: add more tests; error handling * feat: WIP create from React UI * feat: PR feedback [run CI] * feat: try committing snapshot cache changes [run ci] * feat: try re-generating snapshot [run ci] * fix build * regenerate cache on darwin * update caches * Revert "feat: try re-generating snapshot [run ci]" This reverts commit d763e1f. * fix typing error * types * fix test * chore: try using [email protected] * update test * regen linux snapshot * update snapshots for darwin * re-gen linux snapshot * yarn install * update snapshots * update snapshot metadata * update snapshots due to babel deps changing slightly * make react docgen a dep * update tests * revert * snapshots again?? * revert * update * update * try change snapshot * change snap * update snap * feat: remove unnecessary ts-ignore * feat: add more test cases * feat: create CodegenActions; other minor refactors * feat: continue UI work * feat: ignore config and Cypress-related files * feat: PR feedback * update Vue component link * merge in default export work * consolidate graphql queries * other misc feedback * use network-only policy to fetch files; include cypress/ dir for code gen candidates; fix type error * add basic e2e test * fix app integration tests * refactor and fix app component and webpack dev server tests * add error state; fix unit tests [skip ci] * simplify generator show logic [skip ci] * more testing * fix types * style updates [skip ci] * fix error state [skip ci] * fix list padding [skip ci] * use slots (#25079) * add more tests; fix unit tests * fix types * fix test describe * add percy snapshots for new list * update trouble rendering banner link [skip ci] * use collapsible component * use button for component list items * fix tests * build binaries * revert changes to circle config * remove eslintignore and extra loading div [skip ci] because we know it will fail * revert changes to framework glob patterns [skip ci] Co-authored-by: Ryan Manuel <[email protected]> Co-authored-by: Lachlan Miller <[email protected]>
1 parent f054bdc commit f61688c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1561
-287
lines changed

npm/webpack-dev-server/cypress/e2e/react.cy.ts

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ for (const project of WEBPACK_REACT) {
129129

130130
// 4. recreate spec, with same name as removed spec
131131
cy.findByTestId('new-spec-button').click()
132+
cy.findByRole('button', { name: 'Create new empty spec' }).should('be.visible').click()
133+
132134
cy.findByRole('dialog').within(() => {
133135
cy.get('input').clear().type('src/App.cy.jsx')
134136
cy.contains('button', 'Create spec').click()

packages/app/cypress/e2e/create-from-component.cy.ts

+168-24
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json'
22
import { getPathForPlatform } from '../../src/paths'
33

4-
function validateCreateFromComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
4+
function validateCreateFromVueComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
55
beforeEach(beforeEachFn)
66

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

85-
cy.findByText('<HelloWorld ... />', { timeout: 10000 }).should('be.visible')
85+
cy.waitForSpecToFinish({ passCount: 1 })
86+
})
87+
}
88+
89+
function validateCreateFromReactComponentCard (beforeEachFn: () => void, expectedSpecPath: string) {
90+
beforeEach(beforeEachFn)
91+
92+
it('Shows create from component card for React projects', () => {
93+
cy.get('@ComponentCard')
94+
.within(() => {
95+
cy.findByRole('button', {
96+
name: 'Create from component',
97+
}).should('be.visible')
98+
.and('not.be.disabled')
99+
})
100+
})
101+
102+
it('Can be closed with the x button', () => {
103+
cy.get('@ComponentCard').click()
104+
105+
cy.findByRole('button', { name: 'Close' }).as('DialogCloseButton')
106+
107+
cy.get('@DialogCloseButton').click()
108+
cy.findByRole('dialog', {
109+
name: 'Choose a component',
110+
}).should('not.exist')
111+
})
112+
113+
it('Lists files in the project', () => {
114+
cy.get('@ComponentCard').click()
115+
116+
cy.findByText('5 matches').should('be.visible')
117+
118+
cy.findByText('App').should('be.visible')
119+
cy.findByText('index').should('be.visible')
120+
})
121+
122+
it('Allows for the user to search through their components', () => {
123+
cy.get('@ComponentCard').click()
124+
125+
cy.findByText('*.{js,jsx,ts,tsx}').should('be.visible')
126+
cy.findByText('5 matches').should('be.visible')
127+
cy.findByLabelText('file-name-input').type('App')
128+
129+
cy.findByText('App').should('be.visible')
130+
cy.findByText('1 of 5 matches').should('be.visible')
131+
cy.findByText('index').should('not.exist')
132+
cy.findByText('component').should('not.exist')
133+
})
134+
135+
it('shows \'No components found\' if there are no exported components', () => {
136+
cy.get('@ComponentCard').click()
137+
138+
cy.findByText('index').should('be.visible').click()
139+
140+
cy.findByTestId('react-component-row').should('not.exist')
141+
cy.contains('No components found').should('be.visible')
142+
})
143+
144+
it('shows \'Unable to parse file\' if there was an error parsing the file', () => {
145+
cy.get('@ComponentCard').click()
146+
147+
// This component has a syntax error so we will fail to parse it
148+
cy.findByText('Invalid').should('be.visible').click()
149+
150+
cy.findByTestId('react-component-row').should('not.exist')
151+
cy.contains('Unable to parse file').should('be.visible')
152+
})
153+
154+
it('shows success modal when component spec is created', () => {
155+
cy.get('@ComponentCard').click()
156+
157+
// Expand the row
158+
cy.findByText('App').should('be.visible').click()
159+
160+
// Click on 'app' component
161+
cy.findByTestId('react-component-row').should('contain', 'App').click()
162+
163+
cy.findByRole('dialog', {
164+
name: defaultMessages.createSpec.successPage.header,
165+
}).as('SuccessDialog').within(() => {
166+
cy.contains(getPathForPlatform(expectedSpecPath)).should('be.visible')
167+
cy.findByRole('button', { name: 'Close' }).should('be.visible')
168+
169+
cy.findByRole('link', { name: 'Okay, run the spec' })
170+
.should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`)
171+
172+
cy.findByRole('button', { name: 'Create another spec' }).click()
173+
})
174+
175+
// 'Create from component' card appears again when the user selects "create another spec"
176+
cy.findByText('Create from component').should('be.visible')
177+
})
178+
179+
it('runs generated spec', () => {
180+
cy.get('@ComponentCard').click()
181+
182+
// Expand the row
183+
cy.findByText('App').should('be.visible').click()
184+
185+
// Click on 'app' component
186+
cy.findByTestId('react-component-row').should('contain', 'App').click()
187+
188+
cy.findByRole('dialog', {
189+
name: defaultMessages.createSpec.successPage.header,
190+
}).as('SuccessDialog').within(() => {
191+
cy.contains(getPathForPlatform(expectedSpecPath)).should('be.visible')
192+
cy.findByRole('button', { name: 'Close' }).should('be.visible')
193+
194+
// There appears to be a race condition here where sometimes we try to run the spec
195+
// before the file has been written to. Waiting here for 1 second resolves the issue.
196+
cy.wait(1000)
197+
198+
cy.findByRole('link', { name: 'Okay, run the spec' })
199+
.should('have.attr', 'href', `#/specs/runner?file=${expectedSpecPath}`).click()
200+
})
201+
202+
cy.waitForSpecToFinish({ passCount: 1 })
86203
})
87204
}
88205

89206
describe('Create from component card', () => {
90-
context('project with default spec pattern', () => {
91-
validateCreateFromComponentCard(() => {
92-
cy.scaffoldProject('no-specs-vue-2')
93-
cy.openProject('no-specs-vue-2')
94-
cy.startAppServer('component')
95-
cy.visitApp()
96-
97-
cy.findAllByTestId('card').eq(0).as('ComponentCard')
98-
}, 'src/components/HelloWorld.cy.js')
99-
})
100-
101-
context('project with custom spec pattern', () => {
102-
validateCreateFromComponentCard(() => {
103-
cy.scaffoldProject('no-specs-vue-2')
104-
cy.openProject('no-specs-vue-2', ['--config-file', 'cypress-custom-spec-pattern.config.js'])
105-
cy.startAppServer('component')
106-
cy.visitApp()
107-
108-
cy.findByText('New spec').click()
109-
cy.findAllByTestId('card').eq(0).as('ComponentCard')
110-
}, 'src/specs-folder/HelloWorld.cy.js')
207+
context('Vue', () => {
208+
context('project with default spec pattern', () => {
209+
validateCreateFromVueComponentCard(() => {
210+
cy.scaffoldProject('no-specs-vue-2')
211+
cy.openProject('no-specs-vue-2')
212+
cy.startAppServer('component')
213+
cy.visitApp()
214+
215+
cy.findAllByTestId('card').eq(0).as('ComponentCard')
216+
}, 'src/components/HelloWorld.cy.js')
217+
})
218+
219+
context('project with custom spec pattern', () => {
220+
validateCreateFromVueComponentCard(() => {
221+
cy.scaffoldProject('no-specs-vue-2')
222+
cy.openProject('no-specs-vue-2', ['--config-file', 'cypress-custom-spec-pattern.config.js'])
223+
cy.startAppServer('component')
224+
cy.visitApp()
225+
226+
cy.findByText('New spec').click()
227+
cy.findAllByTestId('card').eq(0).as('ComponentCard')
228+
}, 'src/specs-folder/HelloWorld.cy.js')
229+
})
230+
})
231+
232+
context('React', () => {
233+
context('project with default spec pattern', () => {
234+
validateCreateFromReactComponentCard(() => {
235+
cy.scaffoldProject('no-specs')
236+
cy.openProject('no-specs')
237+
cy.startAppServer('component')
238+
cy.visitApp()
239+
240+
cy.findAllByTestId('card').eq(0).as('ComponentCard')
241+
}, 'src/App.cy.jsx')
242+
})
243+
244+
context('project with custom spec pattern', () => {
245+
validateCreateFromReactComponentCard(() => {
246+
cy.scaffoldProject('no-specs')
247+
cy.openProject('no-specs', ['--config-file', 'cypress-custom-spec-pattern.config.ts'])
248+
cy.startAppServer('component')
249+
cy.visitApp()
250+
251+
cy.findByText('New spec').click()
252+
cy.findAllByTestId('card').eq(0).as('ComponentCard')
253+
}, 'src/specs-folder/App.cy.jsx')
254+
})
111255
})
112256
})

packages/app/cypress/e2e/specs.cy.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,12 @@ describe('App: Specs', () => {
524524
})
525525
})
526526

527+
function selectEmptySpecCard () {
528+
cy.findAllByTestId('card').should('have.length', 2)
529+
cy.findByRole('button', { name: 'Create from component' }).should('be.visible')
530+
cy.findByRole('button', { name: 'Create new empty spec' }).should('be.visible').click()
531+
}
532+
527533
describe('Testing Type: Component', {
528534
viewportHeight: 768,
529535
viewportWidth: 1024,
@@ -535,7 +541,7 @@ describe('App: Specs', () => {
535541
cy.startAppServer('component')
536542
cy.visitApp()
537543

538-
cy.findAllByTestId('card').eq(0).as('EmptyCard')
544+
cy.findAllByTestId('card').eq(1).as('EmptyCard')
539545
})
540546

541547
it('shows create new empty spec card', () => {
@@ -589,7 +595,9 @@ describe('App: Specs', () => {
589595

590596
// 'Create a new spec' dialog presents with options when user indicates they want to create
591597
// another spec.
592-
cy.findByRole('dialog', { name: 'Enter the path for your new spec' }).should('be.visible')
598+
cy.findAllByTestId('card').should('have.length', 2)
599+
cy.findByRole('button', { name: 'Create new empty spec' }).should('be.visible')
600+
cy.findByRole('button', { name: 'Create from component' }).should('be.visible')
593601
})
594602

595603
it('navigates to spec runner when selected', () => {
@@ -628,7 +636,7 @@ describe('App: Specs', () => {
628636
})
629637

630638
cy.contains('Review the docs')
631-
.should('have.attr', 'href', 'https://on.cypress.io/mount')
639+
.should('have.attr', 'href', 'https://on.cypress.io/styling-components')
632640

633641
cy.log('should not contain the link if you navigate away and back')
634642
cy.get('body').type('f')
@@ -709,13 +717,17 @@ describe('App: Specs', () => {
709717
it('shows new spec button to start creation workflow', () => {
710718
cy.findByRole('button', { name: 'New spec', exact: false }).click()
711719

720+
selectEmptySpecCard()
721+
712722
cy.findByRole('dialog', { name: 'Enter the path for your new spec' }).should('be.visible')
713723
})
714724

715725
it('shows create first spec page with create empty option and goes back if it is cancel', () => {
716726
cy.findByRole('button', { name: 'New spec', exact: false }).click()
717727

718-
cy.contains('Cancel').click()
728+
selectEmptySpecCard()
729+
730+
cy.contains('Back').click()
719731

720732
cy.findByRole('dialog', { name: 'Enter the path for your new spec' }).should('not.exist')
721733
})
@@ -738,6 +750,8 @@ describe('App: Specs', () => {
738750

739751
cy.findByRole('button', { name: 'New spec' }).click()
740752

753+
selectEmptySpecCard()
754+
741755
cy.findByRole('dialog', {
742756
name: 'Enter the path for your new spec',
743757
}).within(() => {

packages/app/src/paths.ts

+4
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ export function getPathForPlatform (posixPath?: string) {
1818

1919
return posixPath
2020
}
21+
22+
export function posixify (path: string): string {
23+
return path.replace(/\\/g, '/')
24+
}

packages/app/src/runner/SpecRunnerHeaderOpenMode.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@
153153
dismissible
154154
>
155155
<template #title>
156-
<ExternalLink href="https://on.cypress.io/mount">
157-
<i-cy-book_x16 class="inline-block icon-dark-indigo-500 icon-light-indigo-200" />
156+
<i-cy-book_x16 class="pr-2px inline-block icon-dark-indigo-500 icon-light-indigo-200" />
157+
<ExternalLink href="https://on.cypress.io/styling-components">
158158
{{ t('runner.header.reviewDocs') }}
159159
</ExternalLink>
160160
{{ t('runner.header.troubleRendering') }}

0 commit comments

Comments
 (0)