Skip to content

Commit 166b694

Browse files
astone123ryanthemanuellmiller1990ZachJW34
authored
feat: create from React component (#25168)
* feat: server logic for create from React component (#24881) Co-authored-by: Ryan Manuel <[email protected]> Co-authored-by: Lachlan Miller <[email protected]> * fix: add default export detection (#24954) Co-authored-by: astone123 <[email protected]> * update cache * update yarn.lock to fix builds * fix: compilation with webpack preprocessor * 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]> * feat: pass parser options to allow parsing of tsx files (#25145) * fix create from component e2e test * build binaries [run ci] * fix component tests [run ci] * regen windows snapshot Co-authored-by: Ryan Manuel <[email protected]> Co-authored-by: Lachlan Miller <[email protected]> Co-authored-by: Zachary Williams <[email protected]>
1 parent fcc49b5 commit 166b694

File tree

84 files changed

+7568
-4529
lines changed

Some content is hidden

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

84 files changed

+7568
-4529
lines changed

.circleci/workflows.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mainBuildFilters: &mainBuildFilters
2828
only:
2929
- develop
3030
- /^release\/\d+\.\d+\.\d+$/
31-
- 'matth/fix/electron-video-performance'
31+
- 'feature/create-from-react-component'
3232

3333
# usually we don't build Mac app - it takes a long time
3434
# but sometimes we want to really confirm we are doing the right thing
@@ -37,15 +37,15 @@ macWorkflowFilters: &darwin-workflow-filters
3737
when:
3838
or:
3939
- equal: [ develop, << pipeline.git.branch >> ]
40-
- equal: [ 'matth/fix/electron-video-performance', << pipeline.git.branch >> ]
40+
- equal: [ 'feature/create-from-react-component', << pipeline.git.branch >> ]
4141
- matches:
4242
pattern: /^release\/\d+\.\d+\.\d+$/
4343
value: << pipeline.git.branch >>
4444
linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
4545
when:
4646
or:
4747
- equal: [ develop, << pipeline.git.branch >> ]
48-
- equal: [ 'matth/fix/electron-video-performance', << pipeline.git.branch >> ]
48+
- equal: [ 'feature/create-from-react-component', << pipeline.git.branch >> ]
4949
- matches:
5050
pattern: /^release\/\d+\.\d+\.\d+$/
5151
value: << pipeline.git.branch >>
@@ -63,7 +63,7 @@ windowsWorkflowFilters: &windows-workflow-filters
6363
when:
6464
or:
6565
- equal: [ develop, << pipeline.git.branch >> ]
66-
- equal: [ 'matth/fix/electron-video-performance', << pipeline.git.branch >> ]
66+
- equal: [ 'feature/create-from-react-component', << pipeline.git.branch >> ]
6767
- matches:
6868
pattern: /^release\/\d+\.\d+\.\d+$/
6969
value: << pipeline.git.branch >>
@@ -129,7 +129,7 @@ commands:
129129
- run:
130130
name: Check current branch to persist artifacts
131131
command: |
132-
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "matth/fix/electron-video-performance" ]]; then
132+
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feature/create-from-react-component" ]]; then
133133
echo "Not uploading artifacts or posting install comment for this branch."
134134
circleci-agent step halt
135135
fi

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

Lines changed: 2 additions & 0 deletions
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()

npm/webpack-preprocessor/__snapshots__/compilation.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
exports['webpack preprocessor - e2e correctly preprocesses the file 1'] = `
22
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)}));
3-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZV9zcGVjX291dHB1dC5qcyIsIm1hcHBpbmdzIjoiQUFBQUEsR0FBRyxhQUFhLFdBR2RDLE9BRmdCLEdBRU5DLEdBQUdDLE1BQU0sR0FDbkJGLE9BSG1CLEdBR1RDLEdBQUdDLE1BQU0sR0FDbkJGLE9BQU9HLEtBQUtDLElBQUwsTUFBQUQsS0FBWSxDQUFDLEVBQUcsS0FBS0YsR0FBR0MsTUFBTSIsInNvdXJjZXMiOlsid2VicGFjazovL0BjeXByZXNzL3dlYnBhY2stcHJlcHJvY2Vzc29yLy4vdGVzdC9fdGVzdC1vdXRwdXQvZXhhbXBsZV9zcGVjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIml0KCdpcyBhIHRlc3QnLCAoKSA9PiB7XG4gIGNvbnN0IFthLCBiXSA9IFsxLCAyXVxuXG4gIGV4cGVjdChhKS50by5lcXVhbCgxKVxuICBleHBlY3QoYikudG8uZXF1YWwoMilcbiAgZXhwZWN0KE1hdGgubWluKC4uLlszLCA0XSkpLnRvLmVxdWFsKDMpXG59KVxuIl0sIm5hbWVzIjpbIml0IiwiZXhwZWN0IiwidG8iLCJlcXVhbCIsIk1hdGgiLCJtaW4iXSwic291cmNlUm9vdCI6IiJ9
3+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhhbXBsZV9zcGVjX291dHB1dC5qcyIsIm1hcHBpbmdzIjoiQUFBQUEsR0FBRyxhQUFhLFdBR2RDLE9BRmdCLEdBRU5DLEdBQUdDLE1BQU0sR0FDbkJGLE9BSG1CLEdBR1RDLEdBQUdDLE1BQU0sR0FDbkJGLE9BQU9HLEtBQUtDLElBQUcsTUFBUkQsS0FBWSxDQUFDLEVBQUcsS0FBS0YsR0FBR0MsTUFBTSIsInNvdXJjZXMiOlsid2VicGFjazovL0BjeXByZXNzL3dlYnBhY2stcHJlcHJvY2Vzc29yLy4vdGVzdC9fdGVzdC1vdXRwdXQvZXhhbXBsZV9zcGVjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIml0KCdpcyBhIHRlc3QnLCAoKSA9PiB7XG4gIGNvbnN0IFthLCBiXSA9IFsxLCAyXVxuXG4gIGV4cGVjdChhKS50by5lcXVhbCgxKVxuICBleHBlY3QoYikudG8uZXF1YWwoMilcbiAgZXhwZWN0KE1hdGgubWluKC4uLlszLCA0XSkpLnRvLmVxdWFsKDMpXG59KVxuIl0sIm5hbWVzIjpbIml0IiwiZXhwZWN0IiwidG8iLCJlcXVhbCIsIk1hdGgiLCJtaW4iXSwic291cmNlUm9vdCI6IiJ9
44
`
55

66
exports['webpack preprocessor - e2e has less verbose syntax error 1'] = `

npm/webpack-preprocessor/test/unit/cross-origin-callback-loader.spec.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ describe('./lib/cross-origin-callback-loader', () => {
220220
expectAddFileSource(store).to.equal(stripIndent`
221221
__cypressCrossOriginCallback = () => {
222222
const utils = require('../support/utils');
223-
224223
utils.foo();
225224
}`)
226225
})
@@ -230,9 +229,7 @@ describe('./lib/cross-origin-callback-loader', () => {
230229
`it('test', () => {
231230
cy.origin('http://www.foobar.com:3500', () => {
232231
require('../support/commands')
233-
234232
const utils = require('../support/utils')
235-
236233
const _ = require('lodash')
237234
})
238235
})`,
@@ -241,9 +238,7 @@ describe('./lib/cross-origin-callback-loader', () => {
241238
expectAddFileSource(store).to.equal(stripIndent`
242239
__cypressCrossOriginCallback = () => {
243240
require('../support/commands');
244-
245241
const utils = require('../support/utils');
246-
247242
const _ = require('lodash');
248243
}`)
249244
})
@@ -270,9 +265,7 @@ describe('./lib/cross-origin-callback-loader', () => {
270265
`it('test', () => {
271266
cy.origin('http://www.foobar.com:3500', () => {
272267
const someVar = 'someValue'
273-
274268
const result = require('./fn')(someVar)
275-
276269
expect(result).to.equal('mutated someVar')
277270
})
278271
})`,
@@ -281,9 +274,7 @@ describe('./lib/cross-origin-callback-loader', () => {
281274
expectAddFileSource(store).to.equal(stripIndent`
282275
__cypressCrossOriginCallback = () => {
283276
const someVar = 'someValue';
284-
285277
const result = require('./fn')(someVar);
286-
287278
expect(result).to.equal('mutated someVar');
288279
}`)
289280
})
@@ -293,7 +284,6 @@ describe('./lib/cross-origin-callback-loader', () => {
293284
`it('test', () => {
294285
cy.origin('http://www.foobar.com:3500', { args: { foo: 'foo'}}, ({ foo }) => {
295286
const result = require('./fn')(foo)
296-
297287
expect(result).to.equal('mutated someVar')
298288
})
299289
})`,
@@ -304,7 +294,6 @@ describe('./lib/cross-origin-callback-loader', () => {
304294
foo
305295
}) => {
306296
const result = require('./fn')(foo);
307-
308297
expect(result).to.equal('mutated someVar');
309298
}`)
310299
})

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

Lines changed: 168 additions & 24 deletions
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,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(2000)
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
})

0 commit comments

Comments
 (0)