From 03b80da1b8741a929868d74771832dde14fcba2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ovidiu=20Chereche=C8=99?= Date: Mon, 17 Apr 2023 13:12:38 +0300 Subject: [PATCH] Lazy mode (#1443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add lazy fixture loader * Fix rendering * WIP: Handle both lazy and static module wrappers * Update FixtureConnect.tsx * Merge renderKey with fixture state * Create dual user deps templates * Add Vite React plugin * Add lazy config flag * Group module wrappers into union type * Fix types * Clean up unused type * Update FixtureConnect.tsx * Update FixtureConnect.tsx * Clean up types and modules * Remove duplicate code * Simplify components * Rename files * Extract common type * Reset fixture state state when fixture ID changes * Update useRendererResponse.ts * Update types.ts * Refactor renderer tests * Delete FixtureLoader folder * Simulate async communication between renderer and parent * Test: renders first named fixture * Rename renderer helper * Expand fixture names in Playground fixture list when lazy multi fixture is selected * Fix renderer header when multi fixture is selected by path * Change test names * Set lazy mode in examples * Add lazy CLI param * Fix yargs mock * Format start command * Add lazy tests * Update test.yml * Update test.yml * Update test.yml * Send lazy flag to Cypress * Rename file * Update createRendererConnectTestApi.ts * Wrap fixtures in test helper * Make tests more resilient * Update mountTestRenderer.tsx * Make renderer tests more flexible * Test lazy renderer * Add more lazy renderer tests * Update DecoratedFixture.tsx * Add new line * Update userDepsTemplate.test.ts.snap * Update test.yml * Clean up setTimeout wrap * Update getFixtureList.ts * Don’t send fixtureStateChanged response because of setFixtureStateChange request * Refactor types --- .github/workflows/test.yml | 23 +- cypress/support/envVars.ts | 4 + cypress/tests/native.ts | 27 +- docs/roadmap/README.md | 5 +- .../collapseFixtureDirs.test.ts | 44 +-- .../createFixtureTree/collapseIndexes.test.ts | 32 +- .../collapseNamedIndexes.test.ts | 12 +- .../collapseOuterDirs.test.ts | 20 +- .../createRawFixtureTree.test.ts | 16 +- .../createFixtureTree/createRawFixtureTree.ts | 15 +- .../hideFixtureSuffix.test.ts | 8 +- .../src/fixtureTree/flattenFixtureTree.tsx | 6 +- .../src/fixtureTree/types.ts | 5 +- packages/react-cosmos-core/src/index.ts | 12 +- .../src/renderer/DecoratedFixture.tsx | 66 ++++ .../classState/decorateFixtureRefs/index.ts | 0 .../decorateFixtureRefs/isRefSupported.ts | 0 .../FixtureCapture/classState/index.ts | 2 +- .../FixtureCapture/classState/shared.ts | 0 .../classState/useFixtureState.ts | 12 +- .../classState/useReadClassState.ts | 10 +- .../FixtureCapture/index.tsx | 2 +- .../FixtureCapture/props/index.ts | 12 +- .../FixtureCapture/props/useFixtureProps.ts | 8 +- .../shared/findRelevantElementPaths.ts | 0 .../shared/nodeTree/__tests__/find.tsx | 0 .../shared/nodeTree/__tests__/set.tsx | 0 .../shared/nodeTree/findElementPaths.ts | 2 +- .../shared/nodeTree/getElementAtPath.ts | 2 +- .../FixtureCapture/shared/nodeTree/index.ts | 0 .../shared/nodeTree/setElementAtPath.ts | 2 +- .../FixtureCapture/shared/nodeTree/shared.ts | 0 .../src/renderer/FixtureConnect.tsx | 101 ++++++ .../src/renderer/FixtureLoader.tsx | 67 ++++ .../renderer/FixtureLoader/FixtureLoader.tsx | 321 ------------------ .../FixtureLoader/FixtureProvider.tsx | 66 ---- .../FixtureLoader/testHelpers/index.ts | 20 -- .../FixtureLoader/testHelpers/postMessage.tsx | 54 --- .../FixtureLoader/testHelpers/shared.tsx | 196 ----------- .../FixtureLoader/testHelpers/webSockets.tsx | 75 ---- .../FixtureLoader/testHelpers/wrapFixture.ts | 18 - .../renderer/FixtureStateChangeResponse.tsx | 49 +++ .../renderer/LazyFixtureListItemUpdate.tsx | 30 ++ .../src/renderer/LazyFixtureLoader.tsx | 85 +++++ .../__tests__/childrenTransition.tsx | 31 +- .../classStateFixtureReselectMocked.tsx | 16 +- .../classStateFixtureReselectUnmocked.tsx | 16 +- .../__tests__/classStateMocked.tsx | 67 ++-- .../__tests__/classStateMockedRef.tsx | 17 +- .../__tests__/classStateMulti.tsx | 22 +- .../__tests__/classStateUnmocked.tsx | 36 +- .../__tests__/decorators.tsx | 16 +- .../__tests__/errorResetCallback.ts | 18 +- .../__tests__/fixtureAutoSelect.ts | 12 +- .../__tests__/fixtureInitialSelect.ts | 14 +- .../__tests__/fixtureInitialSelectLazy.ts | 51 +++ .../__tests__/fixtureList.ts | 20 +- .../__tests__/fixtureListMulti.ts | 10 +- .../__tests__/fixtureListMultiLazy.ts | 40 +++ .../__tests__/fixtureSelect.ts | 47 ++- .../__tests__/fixtureSelectWithState.tsx | 18 +- .../__tests__/fixtureStateChange.tsx | 20 +- .../__tests__/fragmentTransition.tsx | 16 +- .../{FixtureLoader => }/__tests__/props.tsx | 70 ++-- .../__tests__/propsAndState.tsx | 44 +-- .../__tests__/propsAndStateRef.tsx | 21 +- .../__tests__/propsFnChildren.tsx | 18 +- .../__tests__/propsMulti.tsx | 32 +- .../__tests__/propsObjectIdentity.tsx | 30 +- .../__tests__/propsRef.tsx | 30 +- .../__tests__/propsTypeChange.tsx | 18 +- .../{FixtureLoader => }/__tests__/selects.tsx | 42 ++- .../__tests__/selectsNoDefault.tsx | 18 +- .../__tests__/typeFunction.tsx | 12 +- .../__tests__/typeString.tsx | 23 +- .../__tests__/valuesArray.tsx | 24 +- .../__tests__/valuesBoolean.tsx | 38 ++- .../__tests__/valuesHotReload.tsx | 36 +- .../__tests__/valuesNumber.tsx | 38 ++- .../__tests__/valuesObject.tsx | 38 ++- .../__tests__/valuesString.tsx | 42 ++- .../__tests__/viewport.tsx | 24 +- ...Message.ts => createPostMessageConnect.ts} | 4 +- ...bSockets.ts => createWebSocketsConnect.ts} | 4 +- .../FixtureElement.tsx | 0 .../getDecoratedFixtureElement/index.tsx | 4 +- .../fixtureHelpers.ts => getFixture.ts} | 7 +- .../src/renderer/getFixtureList.ts | 40 ++- .../getSortedDecoratorsForFixturePath.ts | 16 +- .../src/renderer/isMultiFixture.ts | 2 +- .../src/renderer/reactTypes.ts | 26 -- .../{types.ts => rendererConnectTypes.ts} | 12 +- .../testHelpers/components.tsx | 0 .../createRendererConnectTestApi.ts | 169 +++++++++ .../testHelpers/createTestRendererConnect.ts | 41 +++ .../testHelpers/fixtureState.ts | 2 +- .../testHelpers/mountTestRenderer.tsx | 123 +++++++ .../src/renderer/testHelpers/testRenderer.ts | 19 ++ .../renderer/testHelpers/wrapActSetTimeout.ts | 14 + .../renderer/testHelpers/wrapDefaultExport.ts | 6 + .../src/renderer/useFixtureModules.ts | 20 ++ .../src/renderer/useLazyFixtureModules.ts | 42 +++ .../src/renderer/useRendererRequest.ts | 105 ++++++ .../src/renderer/useRendererResponse.ts | 33 ++ .../src/renderer/useSelectedFixture.ts | 51 +++ .../src/renderer/userModuleTypes.ts | 49 +++ .../react-cosmos-dom/src/DomFixtureLoader.tsx | 29 +- .../react-cosmos-dom/src/mountDomRenderer.tsx | 11 +- .../src/NativeFixtureLoader.tsx | 20 +- .../react-cosmos-plugin-vite/package.json | 1 + .../src/createViteRendererIndex.ts | 3 + .../src/viteDevServerPlugin.ts | 3 +- .../src/viteExportPlugin.ts | 3 +- .../src/client/index.ts | 7 +- .../src/client/userDeps.ts | 14 +- .../FixtureTree/FixtureTree/FixtureButton.tsx | 6 +- .../FixtureTree/FixtureTree.test.tsx | 32 ++ .../FixtureTree/FixtureTree/FixtureTree.tsx | 19 +- .../FixtureTree/MultiFixtureButton.tsx | 27 +- .../src/plugins/FixtureTree/revealFixture.ts | 5 +- .../__tests__/isValidFixtureSelected.ts | 7 + .../RendererCore/isValidFixtureSelected.ts | 11 +- .../receiveResponse/fixtureListItemUpdate.ts | 21 ++ .../RendererCore/receiveResponse/index.ts | 3 + .../src/plugins/Root/RendererHeader.tsx | 12 +- .../react-cosmos-ui/src/shared/fixtureTree.ts | 21 +- packages/react-cosmos/config.schema.json | 8 +- .../src/cosmosConfig/createCosmosConfig.ts | 11 + .../react-cosmos/src/cosmosConfig/types.ts | 1 + .../src/getFixtures/getFixtures.ts | 4 +- .../react-cosmos/src/testHelpers/mockYargs.ts | 1 + .../userDepsLazyTemplate.test.ts.snap | 61 ++++ .../userDepsTemplate.test.ts.snap | 32 +- .../src/userDeps/generateUserDepsModule.ts | 6 +- .../src/userDeps/getUserModules.ts | 11 +- .../src/userDeps/userDepsLazyTemplate.test.ts | 44 +++ .../src/userDeps/userDepsLazyTemplate.ts | 51 +++ .../src/userDeps/userDepsShared.ts | 37 ++ .../src/userDeps/userDepsTemplate.ts | 62 ++-- packages/react-cosmos/src/utils/cli.ts | 2 +- yarn.lock | 140 ++++++++ 141 files changed, 2434 insertions(+), 1595 deletions(-) create mode 100644 packages/react-cosmos-core/src/renderer/DecoratedFixture.tsx rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/classState/decorateFixtureRefs/index.ts (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/classState/decorateFixtureRefs/isRefSupported.ts (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/classState/index.ts (88%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/classState/shared.ts (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/classState/useFixtureState.ts (94%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/classState/useReadClassState.ts (88%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/index.tsx (88%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/props/index.ts (87%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/props/useFixtureProps.ts (93%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/findRelevantElementPaths.ts (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/__tests__/find.tsx (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/__tests__/set.tsx (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/findElementPaths.ts (92%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/getElementAtPath.ts (92%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/index.ts (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/setElementAtPath.ts (94%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/FixtureCapture/shared/nodeTree/shared.ts (100%) create mode 100644 packages/react-cosmos-core/src/renderer/FixtureConnect.tsx create mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader.tsx delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureLoader.tsx delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureProvider.tsx delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/index.ts delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/postMessage.tsx delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/shared.tsx delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/webSockets.tsx delete mode 100644 packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/wrapFixture.ts create mode 100644 packages/react-cosmos-core/src/renderer/FixtureStateChangeResponse.tsx create mode 100644 packages/react-cosmos-core/src/renderer/LazyFixtureListItemUpdate.tsx create mode 100644 packages/react-cosmos-core/src/renderer/LazyFixtureLoader.tsx rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/childrenTransition.tsx (85%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/classStateFixtureReselectMocked.tsx (74%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/classStateFixtureReselectUnmocked.tsx (72%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/classStateMocked.tsx (82%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/classStateMockedRef.tsx (78%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/classStateMulti.tsx (75%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/classStateUnmocked.tsx (77%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/decorators.tsx (78%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/errorResetCallback.ts (58%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureAutoSelect.ts (69%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureInitialSelect.ts (78%) create mode 100644 packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelectLazy.ts rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureList.ts (77%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureListMulti.ts (61%) create mode 100644 packages/react-cosmos-core/src/renderer/__tests__/fixtureListMultiLazy.ts rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureSelect.ts (72%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureSelectWithState.tsx (61%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fixtureStateChange.tsx (75%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/fragmentTransition.tsx (82%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/props.tsx (73%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsAndState.tsx (76%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsAndStateRef.tsx (76%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsFnChildren.tsx (69%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsMulti.tsx (67%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsObjectIdentity.tsx (73%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsRef.tsx (76%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/propsTypeChange.tsx (73%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/selects.tsx (80%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/selectsNoDefault.tsx (77%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/typeFunction.tsx (56%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/typeString.tsx (58%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/valuesArray.tsx (83%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/valuesBoolean.tsx (76%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/valuesHotReload.tsx (89%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/valuesNumber.tsx (76%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/valuesObject.tsx (84%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/valuesString.tsx (73%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/__tests__/viewport.tsx (75%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader/postMessage.ts => createPostMessageConnect.ts} (86%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader/webSockets.ts => createWebSocketsConnect.ts} (90%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/getDecoratedFixtureElement/FixtureElement.tsx (100%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/getDecoratedFixtureElement/index.tsx (96%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader/fixtureHelpers.ts => getFixture.ts} (64%) delete mode 100644 packages/react-cosmos-core/src/renderer/reactTypes.ts rename packages/react-cosmos-core/src/renderer/{types.ts => rendererConnectTypes.ts} (89%) rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/testHelpers/components.tsx (100%) create mode 100644 packages/react-cosmos-core/src/renderer/testHelpers/createRendererConnectTestApi.ts create mode 100644 packages/react-cosmos-core/src/renderer/testHelpers/createTestRendererConnect.ts rename packages/react-cosmos-core/src/renderer/{FixtureLoader => }/testHelpers/fixtureState.ts (97%) create mode 100644 packages/react-cosmos-core/src/renderer/testHelpers/mountTestRenderer.tsx create mode 100644 packages/react-cosmos-core/src/renderer/testHelpers/testRenderer.ts create mode 100644 packages/react-cosmos-core/src/renderer/testHelpers/wrapActSetTimeout.ts create mode 100644 packages/react-cosmos-core/src/renderer/testHelpers/wrapDefaultExport.ts create mode 100644 packages/react-cosmos-core/src/renderer/useFixtureModules.ts create mode 100644 packages/react-cosmos-core/src/renderer/useLazyFixtureModules.ts create mode 100644 packages/react-cosmos-core/src/renderer/useRendererRequest.ts create mode 100644 packages/react-cosmos-core/src/renderer/useRendererResponse.ts create mode 100644 packages/react-cosmos-core/src/renderer/useSelectedFixture.ts create mode 100644 packages/react-cosmos-core/src/renderer/userModuleTypes.ts create mode 100644 packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/fixtureListItemUpdate.ts create mode 100644 packages/react-cosmos/src/userDeps/__snapshots__/userDepsLazyTemplate.test.ts.snap create mode 100644 packages/react-cosmos/src/userDeps/userDepsLazyTemplate.test.ts create mode 100644 packages/react-cosmos/src/userDeps/userDepsLazyTemplate.ts create mode 100644 packages/react-cosmos/src/userDeps/userDepsShared.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f42f1d869..2548f5ac9e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,17 +36,26 @@ jobs: fail-fast: false matrix: example: [webpack, vite] - os: - - ubuntu - - windows + os: [ubuntu, windows] node-version: [18] + lazy: [false] include: - example: webpack os: ubuntu node-version: 16 + lazy: false - example: webpack os: windows node-version: 16 + lazy: false + - example: webpack + os: ubuntu + node-version: 18 + lazy: true + - example: vite + os: ubuntu + node-version: 18 + lazy: true runs-on: ${{ matrix.os }}-latest steps: - uses: actions/checkout@v3 @@ -58,7 +67,7 @@ jobs: - run: yarn build # Generate static export for domExport test - - run: yarn workspace example-${{ matrix.example }} export + - run: yarn workspace example-${{ matrix.example }} export --lazy=${{ matrix.lazy == true }} - uses: BerniWittmann/background-server-action@v1 with: @@ -67,10 +76,14 @@ jobs: # - DOM dev server # - DOM export # - Native dev server - start: yarn workspace example-${{ matrix.example }} start,yarn workspace example-${{ matrix.example }} serve,yarn workspace example-${{ matrix.example }} native + start: | + yarn workspace example-${{ matrix.example }} start --lazy=${{ matrix.lazy == true }}, + yarn workspace example-${{ matrix.example }} serve, + yarn workspace example-${{ matrix.example }} native --lazy=${{ matrix.lazy == true }} wait-on: 'http://localhost:5000,http://localhost:5001,http://localhost:5002' env: CYPRESS_EXAMPLE_NAME: ${{ matrix.example }} + CYPRESS_LAZY: ${{ matrix.lazy }} - uses: actions/upload-artifact@v3 if: failure() diff --git a/cypress/support/envVars.ts b/cypress/support/envVars.ts index 3b08a3852d..ffbbae5f66 100644 --- a/cypress/support/envVars.ts +++ b/cypress/support/envVars.ts @@ -1,3 +1,7 @@ export function exampleName() { return Cypress.env('EXAMPLE_NAME') as 'webpack' | 'vite'; } + +export function lazy() { + return Cypress.env('LAZY') as 'true' | 'false'; +} diff --git a/cypress/tests/native.ts b/cypress/tests/native.ts index 132abac28b..73b404dbe6 100644 --- a/cypress/tests/native.ts +++ b/cypress/tests/native.ts @@ -1,4 +1,4 @@ -import { exampleName } from '../support/envVars'; +import { exampleName, lazy } from '../support/envVars'; describe('Native', () => { beforeEach(() => { @@ -25,13 +25,13 @@ describe('Native', () => { }); it('has fixture paths', () => { - userDepsContainsModule('src/__fixtures__/HelloWorld.ts'); - userDepsContainsModule('src/Counter.fixture.tsx'); - userDepsContainsModule('src/WelcomeMessage/WelcomeMessage.fixture.tsx'); + containsImport('src/__fixtures__/HelloWorld.ts'); + containsImport('src/Counter.fixture.tsx'); + containsImport('src/WelcomeMessage/WelcomeMessage.fixture.tsx'); }); it('has decorator paths', () => { - userDepsContainsModule('src/WelcomeMessage/cosmos.decorator.tsx'); + containsImport('src/WelcomeMessage/cosmos.decorator.tsx'); }); }); }); @@ -40,9 +40,16 @@ function getUserDepsFile() { return cy.readFile(`examples/${exampleName()}/cosmos.userdeps.js`); } -function userDepsContainsModule(modulePath: string) { - getUserDepsFile().should( - 'match', - new RegExp(`import (fixture|decorator)[0-9]+ from './${modulePath}'`) - ); +function containsImport(modulePath: string) { + if (lazy()) { + getUserDepsFile().should( + 'match', + new RegExp(`import\\('./${modulePath}'\\)`) + ); + } else { + getUserDepsFile().should( + 'match', + new RegExp(`import [a-z0-9]+ from './${modulePath}'`) + ); + } } diff --git a/docs/roadmap/README.md b/docs/roadmap/README.md index 96c81e8370..528fac1784 100644 --- a/docs/roadmap/README.md +++ b/docs/roadmap/README.md @@ -10,9 +10,10 @@ ## Core -- [x] Redesign codebase packages and APIs. -- [ ] Decide what to do with [Lazy fixture importing](https://github.com/react-cosmos/react-cosmos/pull/1313). Salvage what you can and move on. No need to linger if the complexity required to achieve this is unreasonable. Document everything learned for future reference. - [x] Research: ES6 modules (React Cosmos with no bundler and no compiler). +- [x] Redesign codebase packages and APIs. +- [x] Lazy mode [#1313](https://github.com/react-cosmos/react-cosmos/pull/1313) [#1443](https://github.com/react-cosmos/react-cosmos/pull/1443). +- [ ] Isolated mode? ## Plugins diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseFixtureDirs.test.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseFixtureDirs.test.ts index ba5afcd7bf..e9225e6e75 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseFixtureDirs.test.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseFixtureDirs.test.ts @@ -17,16 +17,8 @@ it('collapses fixtures dir', () => { Button: { data: { type: 'multiFixture', - fixtureIds: { - normal: { - path: 'ui/__fixtures__/shared/Button.js', - name: 'normal', - }, - disabled: { - path: 'ui/__fixtures__/shared/Button.js', - name: 'disabled', - }, - }, + path: 'ui/__fixtures__/shared/Button.js', + names: ['normal', 'disabled'], }, }, }, @@ -34,7 +26,7 @@ it('collapses fixtures dir', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/__fixtures__/Dashboard.js' }, + path: 'ui/__fixtures__/Dashboard.js', }, }, }, @@ -55,16 +47,8 @@ it('collapses fixtures dir', () => { Button: { data: { type: 'multiFixture', - fixtureIds: { - normal: { - path: 'ui/__fixtures__/shared/Button.js', - name: 'normal', - }, - disabled: { - path: 'ui/__fixtures__/shared/Button.js', - name: 'disabled', - }, - }, + path: 'ui/__fixtures__/shared/Button.js', + names: ['normal', 'disabled'], }, }, }, @@ -72,7 +56,7 @@ it('collapses fixtures dir', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/__fixtures__/Dashboard.js' }, + path: 'ui/__fixtures__/Dashboard.js', }, }, }, @@ -95,7 +79,7 @@ it('collapses fixtures dir with sibling', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/__fixtures__/Dashboard.js' }, + path: 'ui/__fixtures__/Dashboard.js', }, }, }, @@ -106,7 +90,7 @@ it('collapses fixtures dir with sibling', () => { Button: { data: { type: 'fixture', - fixtureId: { path: 'ui/shared/Button.fixture.js' }, + path: 'ui/shared/Button.fixture.js', }, }, }, @@ -127,7 +111,7 @@ it('collapses fixtures dir with sibling', () => { Button: { data: { type: 'fixture', - fixtureId: { path: 'ui/shared/Button.fixture.js' }, + path: 'ui/shared/Button.fixture.js', }, }, }, @@ -135,7 +119,7 @@ it('collapses fixtures dir with sibling', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/__fixtures__/Dashboard.js' }, + path: 'ui/__fixtures__/Dashboard.js', }, }, }, @@ -155,7 +139,7 @@ it('collapses fixtures dirs at different levels', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: '__fixtures__/Dashboard.js' }, + path: '__fixtures__/Dashboard.js', }, }, }, @@ -169,7 +153,7 @@ it('collapses fixtures dirs at different levels', () => { Button: { data: { type: 'fixture', - fixtureId: { path: 'shared/__fixtures__/Button.js' }, + path: 'shared/__fixtures__/Button.js', }, }, }, @@ -184,7 +168,7 @@ it('collapses fixtures dirs at different levels', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: '__fixtures__/Dashboard.js' }, + path: '__fixtures__/Dashboard.js', }, }, shared: { @@ -193,7 +177,7 @@ it('collapses fixtures dirs at different levels', () => { Button: { data: { type: 'fixture', - fixtureId: { path: 'shared/__fixtures__/Button.js' }, + path: 'shared/__fixtures__/Button.js', }, }, }, diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseIndexes.test.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseIndexes.test.ts index 3290f77868..ec7c800e28 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseIndexes.test.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseIndexes.test.ts @@ -11,7 +11,7 @@ it('collapses index fixture', () => { index: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/index.fixture.js' }, + path: 'Dashboard/index.fixture.js', }, }, }, @@ -24,7 +24,7 @@ it('collapses index fixture', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/index.fixture.js' }, + path: 'Dashboard/index.fixture.js', }, }, }, @@ -42,10 +42,8 @@ it('collapses index multi fixture', () => { index: { data: { type: 'multiFixture', - fixtureIds: { - normal: { path: 'Button/index.fixture.js', name: 'normal' }, - disabled: { path: 'Button/index.fixture.js', name: 'disabled' }, - }, + path: 'Button/index.fixture.js', + names: ['normal', 'disabled'], }, }, }, @@ -58,10 +56,8 @@ it('collapses index multi fixture', () => { Button: { data: { type: 'multiFixture', - fixtureIds: { - normal: { path: 'Button/index.fixture.js', name: 'normal' }, - disabled: { path: 'Button/index.fixture.js', name: 'disabled' }, - }, + path: 'Button/index.fixture.js', + names: ['normal', 'disabled'], }, }, }, @@ -79,13 +75,13 @@ it('does not collapse index fixture with sibling', () => { index: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/index.fixture.js' }, + path: 'Dashboard/index.fixture.js', }, }, Settings: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/Settings.fixture.js' }, + path: 'Dashboard/Settings.fixture.js', }, }, }, @@ -101,13 +97,13 @@ it('does not collapse index fixture with sibling', () => { index: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/index.fixture.js' }, + path: 'Dashboard/index.fixture.js', }, }, Settings: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/Settings.fixture.js' }, + path: 'Dashboard/Settings.fixture.js', }, }, }, @@ -127,7 +123,7 @@ it('only collapses index fixture without sibling', () => { index: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/index.fixture.js' }, + path: 'Dashboard/index.fixture.js', }, }, Settings: { @@ -136,7 +132,7 @@ it('only collapses index fixture without sibling', () => { index: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/Settings/index.fixture.js' }, + path: 'Dashboard/Settings/index.fixture.js', }, }, }, @@ -154,13 +150,13 @@ it('only collapses index fixture without sibling', () => { index: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/index.fixture.js' }, + path: 'Dashboard/index.fixture.js', }, }, Settings: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/Settings/index.fixture.js' }, + path: 'Dashboard/Settings/index.fixture.js', }, }, }, diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseNamedIndexes.test.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseNamedIndexes.test.ts index cff4d5c29e..7a76855424 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseNamedIndexes.test.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseNamedIndexes.test.ts @@ -11,7 +11,7 @@ it('collapses named index fixture', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/Dashboard.fixture.js' }, + path: 'Dashboard/Dashboard.fixture.js', }, }, }, @@ -24,7 +24,7 @@ it('collapses named index fixture', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard/Dashboard.fixture.js' }, + path: 'Dashboard/Dashboard.fixture.js', }, }, }, @@ -45,7 +45,7 @@ it('collapses nested named index fixture', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/Dashboard/Dashboard.fixture.js' }, + path: 'ui/Dashboard/Dashboard.fixture.js', }, }, }, @@ -63,7 +63,7 @@ it('collapses nested named index fixture', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/Dashboard/Dashboard.fixture.js' }, + path: 'ui/Dashboard/Dashboard.fixture.js', }, }, }, @@ -83,7 +83,7 @@ it('collapses named index fixture (case insensitive)', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'dashboard/Dashboard.fixture.js' }, + path: 'dashboard/Dashboard.fixture.js', }, }, }, @@ -96,7 +96,7 @@ it('collapses named index fixture (case insensitive)', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'dashboard/Dashboard.fixture.js' }, + path: 'dashboard/Dashboard.fixture.js', }, }, }, diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseOuterDirs.test.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseOuterDirs.test.ts index 2740961477..408e9bd610 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseOuterDirs.test.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/collapseOuterDirs.test.ts @@ -11,7 +11,7 @@ it('collapses one outer dir', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'src/Dashboard.fixture.js' }, + path: 'src/Dashboard.fixture.js', }, }, }, @@ -24,7 +24,7 @@ it('collapses one outer dir', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'src/Dashboard.fixture.js' }, + path: 'src/Dashboard.fixture.js', }, }, }, @@ -42,10 +42,8 @@ it('collapses one outer dir (multi fixture)', () => { Button: { data: { type: 'multiFixture', - fixtureIds: { - normal: { path: 'src/Button.fixture.js', name: 'normal' }, - disabled: { path: 'src/Button.fixture.js', name: 'disabled' }, - }, + path: 'src/Button.fixture.js', + names: ['normal', 'disabled'], }, }, }, @@ -58,10 +56,8 @@ it('collapses one outer dir (multi fixture)', () => { Button: { data: { type: 'multiFixture', - fixtureIds: { - normal: { path: 'src/Button.fixture.js', name: 'normal' }, - disabled: { path: 'src/Button.fixture.js', name: 'disabled' }, - }, + path: 'src/Button.fixture.js', + names: ['normal', 'disabled'], }, }, }, @@ -85,7 +81,7 @@ it('collapses multiple outer dirs', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'src/ui/admin/Dashboard.fixture.js' }, + path: 'src/ui/admin/Dashboard.fixture.js', }, }, }, @@ -102,7 +98,7 @@ it('collapses multiple outer dirs', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'src/ui/admin/Dashboard.fixture.js' }, + path: 'src/ui/admin/Dashboard.fixture.js', }, }, }, diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.test.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.test.ts index f71005a7a7..8e3d3682aa 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.test.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.test.ts @@ -12,7 +12,7 @@ it('creates tree with fixture', () => { 'Dashboard.fixture': { data: { type: 'fixture', - fixtureId: { path: 'Dashboard.fixture.js' }, + path: 'Dashboard.fixture.js', }, }, }, @@ -33,7 +33,7 @@ it('creates nested tree with fixture', () => { 'Dashboard.fixture': { data: { type: 'fixture', - fixtureId: { path: 'ui/Dashboard.fixture.js' }, + path: 'ui/Dashboard.fixture.js', }, }, }, @@ -56,10 +56,8 @@ it('creates tree with multi fixture', () => { 'Button.fixture': { data: { type: 'multiFixture', - fixtureIds: { - normal: { path: 'Button.fixture.js', name: 'normal' }, - disabled: { path: 'Button.fixture.js', name: 'disabled' }, - }, + path: 'Button.fixture.js', + names: ['normal', 'disabled'], }, }, }, @@ -83,10 +81,8 @@ it('creates nested tree with multi fixture', () => { 'Button.fixture': { data: { type: 'multiFixture', - fixtureIds: { - normal: { path: 'ui/Button.fixture.js', name: 'normal' }, - disabled: { path: 'ui/Button.fixture.js', name: 'disabled' }, - }, + path: 'ui/Button.fixture.js', + names: ['normal', 'disabled'], }, }, }, diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.ts index 8c96e58800..38f0e2be9c 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/createRawFixtureTree.ts @@ -26,14 +26,15 @@ function addFixturePathToTree( injectNode(rootNode, parents, fileName, { data: { type: 'fixture', - fixtureId: { path: fixturePath }, + path: fixturePath, }, }); } else if (fixtureItem.type == 'multi') { injectNode(rootNode, parents, fileName, { data: { type: 'multiFixture', - fixtureIds: createFixtureIds(fixturePath, fixtureItem.fixtureNames), + path: fixturePath, + names: fixtureItem.fixtureNames, }, }); } @@ -55,16 +56,6 @@ function removeFixtureNameExtension(fixtureName: string) { return fixtureName.replace(/\.(j|t)sx?$/, ''); } -function createFixtureIds(fixturePath: string, fixtureNames: string[]) { - return fixtureNames.reduce( - (fixtureIds, fixtureName) => ({ - ...fixtureIds, - [fixtureName]: { path: fixturePath, name: fixtureName }, - }), - {} - ); -} - function injectNode( rootNode: FixtureTreeNode, parents: string[], diff --git a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/hideFixtureSuffix.test.ts b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/hideFixtureSuffix.test.ts index 7a7be40f6c..c85f36a5e9 100644 --- a/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/hideFixtureSuffix.test.ts +++ b/packages/react-cosmos-core/src/fixtureTree/createFixtureTree/hideFixtureSuffix.test.ts @@ -8,7 +8,7 @@ it('hides fixture suffix', () => { 'Dashboard.fixture': { data: { type: 'fixture', - fixtureId: { path: 'Dashboard.fixture.js' }, + path: 'Dashboard.fixture.js', }, }, }, @@ -19,7 +19,7 @@ it('hides fixture suffix', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'Dashboard.fixture.js' }, + path: 'Dashboard.fixture.js', }, }, }, @@ -37,7 +37,7 @@ it('hides nested fixture suffix', () => { 'Dashboard.fixture': { data: { type: 'fixture', - fixtureId: { path: 'ui/Dashboard.fixture.js' }, + path: 'ui/Dashboard.fixture.js', }, }, }, @@ -53,7 +53,7 @@ it('hides nested fixture suffix', () => { Dashboard: { data: { type: 'fixture', - fixtureId: { path: 'ui/Dashboard.fixture.js' }, + path: 'ui/Dashboard.fixture.js', }, }, }, diff --git a/packages/react-cosmos-core/src/fixtureTree/flattenFixtureTree.tsx b/packages/react-cosmos-core/src/fixtureTree/flattenFixtureTree.tsx index f6e4834c8b..638856a598 100644 --- a/packages/react-cosmos-core/src/fixtureTree/flattenFixtureTree.tsx +++ b/packages/react-cosmos-core/src/fixtureTree/flattenFixtureTree.tsx @@ -28,10 +28,10 @@ export function flattenFixtureTree( ); if (childData.type === 'multiFixture') - Object.keys(childData.fixtureIds).forEach(fixtureName => + childData.names.forEach(fixtureName => flatFixtureTree.push({ fileName: childName, - fixtureId: childData.fixtureIds[fixtureName], + fixtureId: { path: childData.path, name: fixtureName }, parents, name: fixtureName, }) @@ -40,7 +40,7 @@ export function flattenFixtureTree( if (childData.type === 'fixture') flatFixtureTree.push({ fileName: childName, - fixtureId: childData.fixtureId, + fixtureId: { path: childData.path }, parents, name: null, }); diff --git a/packages/react-cosmos-core/src/fixtureTree/types.ts b/packages/react-cosmos-core/src/fixtureTree/types.ts index 4057641764..de9a35d03b 100644 --- a/packages/react-cosmos-core/src/fixtureTree/types.ts +++ b/packages/react-cosmos-core/src/fixtureTree/types.ts @@ -1,8 +1,7 @@ -import { FixtureId } from '../fixture/types.js'; import { TreeNode } from '../utils/tree.js'; export type FixtureTreeNode = TreeNode< | { type: 'fileDir' } - | { type: 'fixture'; fixtureId: FixtureId } - | { type: 'multiFixture'; fixtureIds: Record } + | { type: 'fixture'; path: string } + | { type: 'multiFixture'; path: string; names: string[] } >; diff --git a/packages/react-cosmos-core/src/index.ts b/packages/react-cosmos-core/src/index.ts index 18d7550f34..ad1e40b7cd 100644 --- a/packages/react-cosmos-core/src/index.ts +++ b/packages/react-cosmos-core/src/index.ts @@ -10,16 +10,16 @@ export * from './fixtureTree/createFixtureTree/index.js'; export * from './fixtureTree/flattenFixtureTree.js'; export * from './fixtureTree/types.js'; export * from './playground/playgroundUrl.js'; -export * from './renderer/FixtureLoader/FixtureLoader.js'; -export * from './renderer/FixtureLoader/getDecoratedFixtureElement/index.js'; -export * from './renderer/FixtureLoader/postMessage.js'; -export * from './renderer/FixtureLoader/webSockets.js'; +export * from './renderer/createPostMessageConnect.js'; +export * from './renderer/createWebSocketsConnect.js'; +export * from './renderer/FixtureConnect.js'; +export * from './renderer/getDecoratedFixtureElement/index.js'; export * from './renderer/getFixtureList.js'; export * from './renderer/getSortedDecoratorsForFixturePath.js'; -export * from './renderer/reactTypes.js'; export * from './renderer/registerPlaygroundShortcuts.js'; export * from './renderer/rendererConfig.js'; -export * from './renderer/types.js'; +export * from './renderer/rendererConnectTypes.js'; +export * from './renderer/userModuleTypes.js'; export * from './server/cosmosPluginConfig.js'; export * from './server/serverMessage.js'; export * from './server/socketMessage.js'; diff --git a/packages/react-cosmos-core/src/renderer/DecoratedFixture.tsx b/packages/react-cosmos-core/src/renderer/DecoratedFixture.tsx new file mode 100644 index 0000000000..34b4002cf8 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/DecoratedFixture.tsx @@ -0,0 +1,66 @@ +import React, { useMemo } from 'react'; +import { FixtureContext } from '../fixture/FixtureContext.js'; +import { FixtureState, SetFixtureState } from '../fixtureState/types.js'; +import { getDecoratedFixtureElement } from './getDecoratedFixtureElement/index.js'; +import { + ReactDecorator, + ReactDecoratorModule, + ReactFixture, +} from './userModuleTypes.js'; + +type Props = { + fixture: ReactFixture; + systemDecorators: ReactDecorator[]; + userDecoratorModules: ReactDecoratorModule[]; + fixtureState: FixtureState; + setFixtureState: SetFixtureState; + renderKey: number; + onErrorReset?: () => unknown; +}; +export function DecoratedFixture({ + fixture, + systemDecorators, + userDecoratorModules, + fixtureState, + setFixtureState, + renderKey, + onErrorReset = noop, +}: Props) { + // Prevent unintentional renders https://reactjs.org/docs/context.html#caveats + const contextValue = useMemo( + () => ({ fixtureState, setFixtureState }), + [fixtureState, setFixtureState] + ); + + const decoratedFixture = useMemo(() => { + const decorators = [ + ...systemDecorators, + ...userDecoratorModules.map(m => m.default), + ]; + return getDecoratedFixtureElement(fixture, decorators, { + fixtureState, + setFixtureState, + onErrorReset, + }); + }, [ + fixture, + fixtureState, + onErrorReset, + setFixtureState, + systemDecorators, + userDecoratorModules, + ]); + + return ( + + {decoratedFixture} + + ); +} + +function noop() {} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/decorateFixtureRefs/index.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/decorateFixtureRefs/index.ts similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/decorateFixtureRefs/index.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/classState/decorateFixtureRefs/index.ts diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/decorateFixtureRefs/isRefSupported.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/decorateFixtureRefs/isRefSupported.ts similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/decorateFixtureRefs/isRefSupported.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/classState/decorateFixtureRefs/isRefSupported.ts diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/index.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/index.ts similarity index 88% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/index.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/classState/index.ts index af989fcf3d..e9ebd7ab3c 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/index.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/index.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { FixtureDecoratorId } from '../../../../fixtureState/types.js'; +import { FixtureDecoratorId } from '../../../fixtureState/types.js'; import { ElRefs } from './shared.js'; import { useFixtureState } from './useFixtureState.js'; import { useReadClassState } from './useReadClassState.js'; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/shared.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/shared.ts similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/shared.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/classState/shared.ts diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/useFixtureState.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/useFixtureState.ts similarity index 94% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/useFixtureState.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/classState/useFixtureState.ts index cdfcdb348d..2a42314a48 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/useFixtureState.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/useFixtureState.ts @@ -9,20 +9,20 @@ import { useEffect, useRef, } from 'react'; -import { FixtureContext } from '../../../../fixture/FixtureContext.js'; +import { FixtureContext } from '../../../fixture/FixtureContext.js'; import { createFixtureStateClassState, findFixtureStateClassState, getFixtureStateClassState, removeFixtureStateClassState, -} from '../../../../fixtureState/classState.js'; -import { createValues } from '../../../../fixtureState/createValues.js'; -import { extendWithValues } from '../../../../fixtureState/extendWithValues.js'; +} from '../../../fixtureState/classState.js'; +import { createValues } from '../../../fixtureState/createValues.js'; +import { extendWithValues } from '../../../fixtureState/extendWithValues.js'; import { FixtureDecoratorId, FixtureState, -} from '../../../../fixtureState/types.js'; -import { getComponentName } from '../../../../utils/react/getComponentName.js'; +} from '../../../fixtureState/types.js'; +import { getComponentName } from '../../../utils/react/getComponentName.js'; import { findRelevantElementPaths } from '../shared/findRelevantElementPaths.js'; import { decorateFixtureRefs } from './decorateFixtureRefs/index.js'; import { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/useReadClassState.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/useReadClassState.ts similarity index 88% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/useReadClassState.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/classState/useReadClassState.ts index a782b8c970..d4b2f8479f 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/classState/useReadClassState.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/classState/useReadClassState.ts @@ -6,17 +6,17 @@ import { useEffect, useRef, } from 'react'; -import { FixtureContext } from '../../../../fixture/FixtureContext.js'; +import { FixtureContext } from '../../../fixture/FixtureContext.js'; import { findFixtureStateClassState, updateFixtureStateClassState, -} from '../../../../fixtureState/classState.js'; -import { createValues } from '../../../../fixtureState/createValues.js'; -import { extendWithValues } from '../../../../fixtureState/extendWithValues.js'; +} from '../../../fixtureState/classState.js'; +import { createValues } from '../../../fixtureState/createValues.js'; +import { extendWithValues } from '../../../fixtureState/extendWithValues.js'; import { FixtureDecoratorId, FixtureStateClassState, -} from '../../../../fixtureState/types.js'; +} from '../../../fixtureState/types.js'; import { findRelevantElementPaths } from '../shared/findRelevantElementPaths.js'; import { ElRefs } from './shared.js'; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/index.tsx b/packages/react-cosmos-core/src/renderer/FixtureCapture/index.tsx similarity index 88% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/index.tsx rename to packages/react-cosmos-core/src/renderer/FixtureCapture/index.tsx index 0353e8b6db..dece218395 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/index.tsx +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { FixtureDecoratorId } from '../../../fixtureState/types.js'; +import { FixtureDecoratorId } from '../../fixtureState/types.js'; import { useClassStateCapture } from './classState/index.js'; import { usePropsCapture } from './props/index.js'; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/props/index.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/props/index.ts similarity index 87% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/props/index.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/props/index.ts index 0292047513..f84ca3b8e9 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/props/index.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/props/index.ts @@ -1,16 +1,16 @@ import { ReactNode, useContext, useEffect, useRef } from 'react'; -import { FixtureContext } from '../../../../fixture/FixtureContext.js'; -import { createValues } from '../../../../fixtureState/createValues.js'; +import { FixtureContext } from '../../../fixture/FixtureContext.js'; +import { createValues } from '../../../fixtureState/createValues.js'; import { createFixtureStateProps, findFixtureStateProps, getFixtureStateProps, removeFixtureStateProps, updateFixtureStateProps, -} from '../../../../fixtureState/props.js'; -import { FixtureDecoratorId } from '../../../../fixtureState/types.js'; -import { areNodesEqual } from '../../../../utils/react/areNodesEqual.js'; -import { getComponentName } from '../../../../utils/react/getComponentName.js'; +} from '../../../fixtureState/props.js'; +import { FixtureDecoratorId } from '../../../fixtureState/types.js'; +import { areNodesEqual } from '../../../utils/react/areNodesEqual.js'; +import { getComponentName } from '../../../utils/react/getComponentName.js'; import { findRelevantElementPaths } from '../shared/findRelevantElementPaths.js'; import { getElementAtPath, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/props/useFixtureProps.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/props/useFixtureProps.ts similarity index 93% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/props/useFixtureProps.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/props/useFixtureProps.ts index f4ea27901e..5133fed140 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/props/useFixtureProps.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/props/useFixtureProps.ts @@ -1,15 +1,15 @@ import { isEqual, mapValues } from 'lodash-es'; import React from 'react'; -import { extendWithValues } from '../../../../fixtureState/extendWithValues.js'; +import { extendWithValues } from '../../../fixtureState/extendWithValues.js'; import { DEFAULT_RENDER_KEY, findFixtureStateProps, -} from '../../../../fixtureState/props.js'; +} from '../../../fixtureState/props.js'; import { FixtureDecoratorId, FixtureState, -} from '../../../../fixtureState/types.js'; -import { getComponentName } from '../../../../utils/react/getComponentName.js'; +} from '../../../fixtureState/types.js'; +import { getComponentName } from '../../../utils/react/getComponentName.js'; import { findRelevantElementPaths } from '../shared/findRelevantElementPaths.js'; import { getChildrenPath, setElementAtPath } from '../shared/nodeTree/index.js'; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/findRelevantElementPaths.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/findRelevantElementPaths.ts similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/findRelevantElementPaths.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/findRelevantElementPaths.ts diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/__tests__/find.tsx b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/__tests__/find.tsx similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/__tests__/find.tsx rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/__tests__/find.tsx diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/__tests__/set.tsx b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/__tests__/set.tsx similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/__tests__/set.tsx rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/__tests__/set.tsx diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/findElementPaths.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/findElementPaths.ts similarity index 92% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/findElementPaths.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/findElementPaths.ts index 5bf69f71e8..e1e14cab7b 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/findElementPaths.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/findElementPaths.ts @@ -1,6 +1,6 @@ import { flatten } from 'lodash-es'; import React, { Fragment } from 'react'; -import { isReactElement } from '../../../../../utils/react/isReactElement.js'; +import { isReactElement } from '../../../../utils/react/isReactElement.js'; import { getChildrenPath } from './shared.js'; export function findElementPaths( diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/getElementAtPath.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/getElementAtPath.ts similarity index 92% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/getElementAtPath.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/getElementAtPath.ts index bcff9fae3b..933a07e78e 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/getElementAtPath.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/getElementAtPath.ts @@ -1,6 +1,6 @@ import { get } from 'lodash-es'; import { ReactElement, ReactNode } from 'react'; -import { isReactElement } from '../../../../../utils/react/isReactElement.js'; +import { isReactElement } from '../../../../utils/react/isReactElement.js'; import { isRootPath } from './shared.js'; // Why be silent about trying to fetch a node that isn't an element? diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/index.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/index.ts similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/index.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/index.ts diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/setElementAtPath.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/setElementAtPath.ts similarity index 94% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/setElementAtPath.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/setElementAtPath.ts index 1217f1ea19..5775f91ec7 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/setElementAtPath.ts +++ b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/setElementAtPath.ts @@ -1,6 +1,6 @@ import { set } from 'lodash-es'; import { ReactElement, ReactNode } from 'react'; -import { isReactElement } from '../../../../../utils/react/isReactElement.js'; +import { isReactElement } from '../../../../utils/react/isReactElement.js'; import { getExpectedElementAtPath } from './getElementAtPath.js'; import { isRootPath } from './shared.js'; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/shared.ts b/packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/shared.ts similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureCapture/shared/nodeTree/shared.ts rename to packages/react-cosmos-core/src/renderer/FixtureCapture/shared/nodeTree/shared.ts diff --git a/packages/react-cosmos-core/src/renderer/FixtureConnect.tsx b/packages/react-cosmos-core/src/renderer/FixtureConnect.tsx new file mode 100644 index 0000000000..33a5f22a10 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/FixtureConnect.tsx @@ -0,0 +1,101 @@ +import React, { ReactElement } from 'react'; +import { FixtureId } from '../fixture/types.js'; +import { FixtureLoader } from './FixtureLoader.js'; +import { FixtureStateChangeResponse } from './FixtureStateChangeResponse.js'; +import { LazyFixtureLoader } from './LazyFixtureLoader.js'; +import { RendererConnect } from './rendererConnectTypes.js'; +import { useRendererRequest } from './useRendererRequest.js'; +import { useRendererResponse } from './useRendererResponse.js'; +import { ReactDecorator, UserModuleWrappers } from './userModuleTypes.js'; +import { useSelectedFixture } from './useSelectedFixture.js'; + +type Props = { + rendererId: string; + rendererConnect: RendererConnect; + moduleWrappers: UserModuleWrappers; + systemDecorators: ReactDecorator[]; + initialFixtureId?: FixtureId; + selectedFixtureId?: null | FixtureId; + renderMessage?: (msg: string) => ReactElement; + onErrorReset?: () => unknown; +}; +export function FixtureConnect({ + rendererId, + rendererConnect, + moduleWrappers, + systemDecorators, + initialFixtureId, + selectedFixtureId, + onErrorReset, + renderMessage = defaultRenderMessage, +}: Props) { + const { selectedFixture, setSelectedFixture, setFixtureState } = + useSelectedFixture(initialFixtureId, selectedFixtureId); + + useRendererRequest( + rendererId, + rendererConnect, + moduleWrappers, + setSelectedFixture, + onErrorReset + ); + + useRendererResponse( + rendererId, + rendererConnect, + moduleWrappers, + initialFixtureId + ); + + if (!selectedFixture) { + return renderMessage('No fixture selected.'); + } + + const { fixtureId, fixtureState, renderKey } = selectedFixture; + + if (!moduleWrappers.fixtures[fixtureId.path]) { + return renderMessage(`Fixture path not found: ${fixtureId.path}`); + } + + return ( + <> + {moduleWrappers.lazy ? ( + + ) : ( + + )} + + + ); +} + +function defaultRenderMessage(msg: string) { + return <>{msg}; +} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader.tsx b/packages/react-cosmos-core/src/renderer/FixtureLoader.tsx new file mode 100644 index 0000000000..c07e50dbd2 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/FixtureLoader.tsx @@ -0,0 +1,67 @@ +import React, { ReactElement, useMemo } from 'react'; +import { FixtureId } from '../fixture/types.js'; +import { FixtureState, SetFixtureState } from '../fixtureState/types.js'; +import { DecoratedFixture } from './DecoratedFixture.js'; +import { getFixture } from './getFixture.js'; +import { getSortedDecoratorsForFixturePath } from './getSortedDecoratorsForFixturePath.js'; +import { useFixtureModules } from './useFixtureModules.js'; +import { + ByPath, + ReactDecorator, + ReactDecoratorWrapper, + ReactFixtureWrapper, +} from './userModuleTypes.js'; + +type Props = { + fixtureWrapper: ReactFixtureWrapper; + decorators: ByPath; + systemDecorators: ReactDecorator[]; + fixtureId: FixtureId; + fixtureState: FixtureState; + setFixtureState: SetFixtureState; + renderMessage: (msg: string) => ReactElement; + renderKey: number; + onErrorReset?: () => unknown; +}; +export function FixtureLoader({ + fixtureWrapper, + decorators, + systemDecorators, + fixtureId, + fixtureState, + setFixtureState, + renderMessage, + renderKey, + onErrorReset, +}: Props) { + const decoratorWrappers = useMemo( + () => getSortedDecoratorsForFixturePath(fixtureId.path, decorators), + [decorators, fixtureId.path] + ); + + const modules = useFixtureModules( + fixtureId.path, + fixtureWrapper, + decoratorWrappers + ); + + const { fixtureModule, decoratorModules } = modules; + const fixtureExport = fixtureModule.default; + const fixture = getFixture(fixtureExport, fixtureId.name); + + if (typeof fixture === 'undefined') { + return renderMessage(`Invalid fixture ID: ${JSON.stringify(fixtureId)}`); + } + + return ( + + ); +} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureLoader.tsx b/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureLoader.tsx deleted file mode 100644 index c619629278..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureLoader.tsx +++ /dev/null @@ -1,321 +0,0 @@ -import { isEqual } from 'lodash-es'; -import React, { Component, ReactNode } from 'react'; -import { FixtureId } from '../../fixture/types.js'; -import { FixtureState, SetFixtureState } from '../../fixtureState/types.js'; -import { getFixtureListFromWrappers } from '../getFixtureList.js'; -import { - ReactDecorator, - ReactDecorators, - ReactFixtureWrappers, -} from '../reactTypes.js'; -import { - RendererConnect, - RendererRequest, - RendererResponse, - SelectFixtureRequest, - SetFixtureStateRequest, -} from '../types.js'; -import { getFixture } from './fixtureHelpers.js'; -import { FixtureProvider } from './FixtureProvider.js'; - -// TODO: Split into FixtureLoader and FixtureLoaderConnect -export type Props = { - rendererId: string; - rendererConnect: RendererConnect; - fixtures: ReactFixtureWrappers; - initialFixtureId?: FixtureId; - selectedFixtureId?: null | FixtureId; - systemDecorators: ReactDecorator[]; - userDecorators: ReactDecorators; - renderMessage?: (args: { msg: string }) => ReactNode; - onErrorReset?: () => unknown; -}; - -type SelectedFixture = { - fixtureId: FixtureId; - fixtureState: FixtureState; - // Why is this copy of the fixtureState needed? Two reasons: - // - To avoid posting fixtureStateChange messages with no changes from - // the last message - // - To piggy back on React's setState batching and only send a - // fixtureStateChange message when FixtureLoader updates (via cDU), - // instead of posting messages in rapid succession as fixture state - // changes are dispatched by fixture plugins - syncedFixtureState: FixtureState; -}; - -type State = { - selectedFixture: null | SelectedFixture; - // Used to reset FixtureProvider instance on fixturePath change - renderKey: number; -}; - -function getSelectedFixtureState(fixtureId?: FixtureId | null) { - if (!fixtureId) return null; - return { - fixtureId: fixtureId, - fixtureState: {}, - syncedFixtureState: {}, - }; -} - -export class FixtureLoader extends Component { - state: State = { - selectedFixture: getSelectedFixtureState( - this.props.selectedFixtureId || this.props.initialFixtureId - ), - renderKey: 0, - }; - - unsubscribe: null | (() => unknown) = null; - - componentDidMount() { - if (!this.props.selectedFixtureId) { - const { rendererConnect } = this.props; - this.unsubscribe = rendererConnect.onMessage(this.handleRequest); - this.postReadyState(); - } - } - - componentWillUnmount() { - if (this.unsubscribe) this.unsubscribe(); - } - - componentDidUpdate(prevProps: Props) { - const { fixtures } = this.props; - if (!isEqual(fixtures, prevProps.fixtures)) { - this.postFixtureListUpdate(); - } - - const { selectedFixture } = this.state; - if (selectedFixture) { - const { fixtureId, fixtureState, syncedFixtureState } = selectedFixture; - if (fixtureId && !isEqual(fixtureState, syncedFixtureState)) { - this.postFixtureStateChange(fixtureId, fixtureState); - this.updateSyncedFixtureState(fixtureState); - } - } - } - - shouldComponentUpdate(prevProps: Props, prevState: State) { - // This check exists mainly to prevent updating the fixture tree when - // fixture state setters resulted in no fixture state change - return !isEqual(this.props, prevProps) || !isEqual(this.state, prevState); - } - - render() { - const { selectedFixture } = this.state; - if (!selectedFixture) { - return this.renderMessage('No fixture selected.'); - } - - const { fixtures } = this.props; - const { fixtureId, fixtureState } = selectedFixture; - - // Falsy check doesn't do because fixtures can be any Node, including - // null or undefined. - if (!fixtures.hasOwnProperty(fixtureId.path)) { - return this.renderMessage(`Fixture path not found: ${fixtureId.path}`); - } - - const fixtureExport = fixtures[fixtureId.path].module.default; - const fixture = getFixture(fixtureExport, fixtureId.name); - - if (typeof fixture === 'undefined') { - return this.renderMessage( - `Invalid fixture ID: ${JSON.stringify(fixtureId)}` - ); - } - - const { systemDecorators, userDecorators, onErrorReset } = this.props; - const { renderKey } = this.state; - return ( - - ); - } - - handleRequest = (msg: RendererRequest) => { - if (msg.type === 'pingRenderers') { - return this.postReadyState(); - } - - if (!msg.payload || msg.payload.rendererId !== this.props.rendererId) { - return; - } - - if (doesRequestChangeFixture(msg)) { - this.fireChangeCallback(); - } - - switch (msg.type) { - case 'selectFixture': - return this.handleSelectFixtureRequest(msg); - case 'unselectFixture': - return this.handleUnselectFixtureRequest(); - case 'setFixtureState': - return this.handleSetFixtureStateRequest(msg); - default: - // This Is Fine™ - // Actually, we can't be angry about getting unrelated messages here - // because we don't do any preliminary message filtering to ignore stuff - // like browser devtools communication, nor do we have any message - // metadata conventions in place to perform such filtering at the moment - } - }; - - handleSelectFixtureRequest({ payload }: SelectFixtureRequest) { - const { fixtureId, fixtureState } = payload; - this.setState({ - selectedFixture: { - fixtureId, - fixtureState, - syncedFixtureState: fixtureState, - }, - renderKey: this.state.renderKey + 1, - }); - } - - handleUnselectFixtureRequest() { - this.setState({ - selectedFixture: null, - renderKey: 0, - }); - } - - handleSetFixtureStateRequest({ payload }: SetFixtureStateRequest) { - const { fixtureId, fixtureState } = payload; - const { selectedFixture } = this.state; - // Ensure fixture state applies to currently selected fixture - if (selectedFixture && isEqual(fixtureId, selectedFixture.fixtureId)) { - this.setState({ - selectedFixture: { - fixtureId, - fixtureState, - syncedFixtureState: fixtureState, - }, - }); - } - } - - postReadyState() { - const { rendererId, initialFixtureId } = this.props; - const fixtures = this.getFixtureList(); - this.postMessage({ - type: 'rendererReady', - payload: initialFixtureId - ? { rendererId, fixtures, initialFixtureId } - : { rendererId, fixtures }, - }); - } - - postFixtureListUpdate() { - const { rendererId } = this.props; - this.postMessage({ - type: 'fixtureListUpdate', - payload: { - rendererId, - fixtures: this.getFixtureList(), - }, - }); - } - - postFixtureStateChange = ( - fixtureId: FixtureId, - fixtureState: FixtureState - ) => { - const { rendererId } = this.props; - this.postMessage({ - type: 'fixtureStateChange', - payload: { - rendererId, - fixtureId, - fixtureState, - }, - }); - }; - - setFixtureState: SetFixtureState = stateUpdate => { - if (!this.state.selectedFixture) { - console.warn( - '[FixtureLoader] Trying to set fixture state with no fixture selected' - ); - return; - } - - // Multiple state changes can be dispatched by fixture plugins at almost - // the same time. Since state changes are batched in React, current state - // (this.state.fixtureState) can be stale at dispatch time, and extending - // it can result in cancelling previous state changes that are queued. - // Using an updater function like ({ prevState }) => nextState ensures - // every state change is honored, regardless of timing. - this.setState(({ selectedFixture }: State) => { - if (!selectedFixture) { - return null; - } - - return { - selectedFixture: { - ...selectedFixture, - fixtureState: stateUpdate(selectedFixture.fixtureState), - }, - }; - }); - }; - - getFixtureList() { - return getFixtureListFromWrappers(this.props.fixtures); - } - - fireChangeCallback() { - const { onErrorReset } = this.props; - if (typeof onErrorReset === 'function') { - onErrorReset(); - } - } - - updateSyncedFixtureState(syncedFixtureState: FixtureState) { - this.setState(({ selectedFixture }) => { - if (!selectedFixture) { - return null; - } - - // Other updates that alter state.selectedFixture can be pending when this - // update is submitted. Those updates will be applied first. With this in - // mind, we use a state setter callback to only override syncedFixtureState - // and keep the latest values of other state parts. - return { - selectedFixture: { - ...selectedFixture, - syncedFixtureState, - }, - }; - }); - } - - postMessage(msg: RendererResponse) { - this.props.rendererConnect.postMessage(msg); - } - - renderMessage(msg: string) { - return typeof this.props.renderMessage !== 'undefined' - ? this.props.renderMessage({ msg }) - : msg; - } -} - -function doesRequestChangeFixture(r: RendererRequest) { - return r.type === 'selectFixture' || r.type === 'unselectFixture'; -} - -function noop() {} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureProvider.tsx b/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureProvider.tsx deleted file mode 100644 index 8229e7642d..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/FixtureProvider.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { ReactNode, useMemo } from 'react'; -import { FixtureContext } from '../../fixture/FixtureContext.js'; -import { FixtureId } from '../../fixture/types.js'; -import { FixtureState, SetFixtureState } from '../../fixtureState/types.js'; -import { getSortedDecoratorsForFixturePath } from '../getSortedDecoratorsForFixturePath.js'; -import { - ReactDecorator, - ReactDecorators, - ReactFixture, -} from '../reactTypes.js'; -import { getDecoratedFixtureElement } from './getDecoratedFixtureElement/index.js'; - -type Props = { - fixtureId: FixtureId; - fixture: ReactFixture; - systemDecorators: ReactDecorator[]; - userDecorators: ReactDecorators; - fixtureState: FixtureState; - setFixtureState: SetFixtureState; - renderMessage?: (args: { msg: string }) => ReactNode; - onErrorReset: () => unknown; -}; - -export function FixtureProvider({ - fixtureId, - fixture, - systemDecorators, - userDecorators, - fixtureState, - setFixtureState, - onErrorReset, -}: Props) { - // Prevent unintentional renders https://reactjs.org/docs/context.html#caveats - const contextValue = useMemo( - () => ({ fixtureState, setFixtureState }), - [fixtureState, setFixtureState] - ); - const decoratorProps = useMemo( - () => ({ fixtureState, setFixtureState, onErrorReset }), - [fixtureState, onErrorReset, setFixtureState] - ); - const decorators = useMemo( - () => mergeDecorators(fixtureId, systemDecorators, userDecorators), - [fixtureId, systemDecorators, userDecorators] - ); - const decoratedFixture = useMemo( - () => getDecoratedFixtureElement(fixture, decorators, decoratorProps), - [decoratorProps, decorators, fixture] - ); - return ( - - {decoratedFixture} - - ); -} - -function mergeDecorators( - fixtureId: FixtureId, - systemDecorators: ReactDecorator[], - userDecorators: ReactDecorators -) { - return [ - ...systemDecorators, - ...getSortedDecoratorsForFixturePath(fixtureId.path, userDecorators), - ]; -} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/index.ts b/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/index.ts deleted file mode 100644 index 89aa6fbaf2..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { mountPostMessage } from './postMessage.js'; -import { FixtureLoaderTestArgs, FixtureLoaderTestCallback } from './shared.js'; -import { mountWebSockets } from './webSockets.js'; - -export function testFixtureLoader( - testName: string, - args: FixtureLoaderTestArgs, - cb: FixtureLoaderTestCallback -) { - const pmTest = () => mountPostMessage(args, cb); - const wsTest = () => mountWebSockets(args, cb); - - if (args.only) { - if (args.only !== 'webSocket') it.only(`[postMessage] ${testName}`, pmTest); - if (args.only !== 'postMessage') it.only(`[webSocket] ${testName}`, wsTest); - } else { - it(`[postMessage] ${testName}`, pmTest); - it(`[webSocket] ${testName}`, wsTest); - } -} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/postMessage.tsx b/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/postMessage.tsx deleted file mode 100644 index 50dbaecdb6..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/postMessage.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { create } from 'react-test-renderer'; -import { FixtureLoader } from '../FixtureLoader.js'; -import { createPostMessageConnect } from '../postMessage.js'; -import { - createRendererConnectMockApi, - FixtureLoaderTestArgs, - FixtureLoaderTestCallback, - RendererMessage, -} from './shared.js'; - -export async function mountPostMessage( - args: FixtureLoaderTestArgs, - cb: FixtureLoaderTestCallback -) { - const onMessage = jest.fn(); - window.addEventListener('message', onMessage, false); - - function getMessages() { - return onMessage.mock.calls.map(call => call[0].data); - } - - function postMessage(msg: RendererMessage) { - parent.postMessage(msg, '*'); - } - - function cleanup() { - window.removeEventListener('message', onMessage); - } - - expect.hasAssertions(); - const renderer = create(getElement(args)); - try { - await cb({ - renderer, - update: newArgs => renderer.update(getElement(newArgs)), - ...createRendererConnectMockApi({ getMessages, postMessage }), - }); - } finally { - renderer.unmount(); - cleanup(); - } -} - -function getElement({ decorators = {}, ...otherArgs }: FixtureLoaderTestArgs) { - return ( - - ); -} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/shared.tsx b/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/shared.tsx deleted file mode 100644 index ef4c41b637..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/shared.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import until from 'async-until'; -import { findLast } from 'lodash-es'; -import { ReactTestRenderer } from 'react-test-renderer'; -import { FixtureId } from '../../../fixture/types.js'; -import { FixtureState } from '../../../fixtureState/types.js'; -import { ReactDecorators, ReactFixtureWrappers } from '../../reactTypes.js'; -import { - FixtureListUpdateResponse, - FixtureStateChangeResponse, - RendererId, - RendererReadyResponse, - RendererRequest, - RendererResponse, - SelectFixtureRequest, - SetFixtureStateRequest, - UnselectFixtureRequest, -} from '../../types.js'; - -export type RendererMessage = RendererResponse | RendererRequest; - -type GetMessages = () => RendererMessage[]; - -export type FixtureLoaderTestArgs = { - rendererId: RendererId; - fixtures: ReactFixtureWrappers; - selectedFixtureId?: null | FixtureId; - initialFixtureId?: FixtureId; - decorators?: ReactDecorators; - only?: boolean | 'postMessage' | 'webSocket'; - onErrorReset?: () => unknown; -}; - -export type RendererConnectMockApi = { - pingRenderers: () => Promise; - selectFixture: (payload: SelectFixtureRequest['payload']) => Promise; - unselectFixture: ( - payload: UnselectFixtureRequest['payload'] - ) => Promise; - setFixtureState: ( - payload: SetFixtureStateRequest['payload'] - ) => Promise; - rendererReady: ( - payload: RendererReadyResponse['payload'] - ) => Promise; - fixtureListUpdate: ( - payload: FixtureListUpdateResponse['payload'] - ) => Promise; - fixtureStateChange: ( - payload: FixtureStateChangeResponse['payload'] - ) => Promise; - getLastFixtureState: () => Promise; -}; - -export type FixtureLoaderTestApi = { - renderer: ReactTestRenderer; - update: (args: FixtureLoaderTestArgs) => void; -} & RendererConnectMockApi; - -export type FixtureLoaderTestCallback = ( - api: FixtureLoaderTestApi -) => Promise; - -type RendererConnectMockArgs = { - getMessages: GetMessages; - postMessage: (msg: RendererMessage) => unknown; -}; - -export function createRendererConnectMockApi( - args: RendererConnectMockArgs -): RendererConnectMockApi { - return { - pingRenderers, - selectFixture, - unselectFixture, - setFixtureState, - rendererReady, - fixtureListUpdate, - fixtureStateChange, - getLastFixtureState, - }; - - async function pingRenderers() { - return postMessage({ - type: 'pingRenderers', - }); - } - - async function selectFixture(payload: SelectFixtureRequest['payload']) { - return postMessage({ - type: 'selectFixture', - payload, - }); - } - - async function unselectFixture(payload: UnselectFixtureRequest['payload']) { - return postMessage({ - type: 'unselectFixture', - payload, - }); - } - - async function setFixtureState(payload: SetFixtureStateRequest['payload']) { - return postMessage({ - type: 'setFixtureState', - payload, - }); - } - - async function rendererReady(payload: RendererReadyResponse['payload']) { - await untilMessage({ - type: 'rendererReady', - payload, - }); - } - - async function fixtureListUpdate( - payload: FixtureListUpdateResponse['payload'] - ) { - await untilMessage({ - type: 'fixtureListUpdate', - payload, - }); - } - - async function fixtureStateChange( - payload: FixtureStateChangeResponse['payload'] - ) { - await untilMessage({ - type: 'fixtureStateChange', - payload, - }); - } - - async function getLastFixtureState() { - const msg = await getLastMessageOfType( - 'fixtureStateChange' - ); - return msg.payload.fixtureState; - } - - async function postMessage(msg: RendererRequest) { - args.postMessage(msg); - } - - async function untilMessage(msg: RendererMessage) { - try { - await until( - () => { - try { - // Support expect.any(constructor) matches - // https://jestjs.io/docs/en/expect#expectanyconstructor - expect(findLastMessageWithType(msg.type)).toEqual(msg); - return true; - } catch (err) { - return false; - } - }, - { timeout: 1000 } - ); - } catch (err) { - expect(getLastMessage()).toEqual(msg); - } - } - - async function getLastMessageOfType( - msgType: string - ): Promise { - let lastMsg = null as null | RendererMessage; - - try { - await until( - () => { - lastMsg = getLastMessage(); - return lastMsg && lastMsg.type === msgType; - }, - { timeout: 1000 } - ); - } finally { - if (!lastMsg || lastMsg.type !== msgType) { - throw new Error(`"${msgType}" message never arrived`); - } - } - - return lastMsg as M; - } - - function getLastMessage(): null | RendererMessage { - const messages = args.getMessages(); - return messages.length === 0 ? null : messages[messages.length - 1]; - } - - function findLastMessageWithType(type: string): null | RendererMessage { - const messages = args.getMessages(); - return findLast(messages, msg => msg.type === type) ?? null; - } -} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/webSockets.tsx b/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/webSockets.tsx deleted file mode 100644 index 396c5beb82..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/webSockets.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { waitFor } from '@testing-library/react'; -import React from 'react'; -import { create } from 'react-test-renderer'; -import { WebSocketServer } from 'ws'; -import { - rendererSocketMessage, - SocketMessage, -} from '../../../server/socketMessage.js'; -import { FixtureLoader } from '../FixtureLoader.js'; -import { createWebSocketsConnect } from '../webSockets.js'; -import { - createRendererConnectMockApi, - FixtureLoaderTestArgs, - FixtureLoaderTestCallback, - RendererMessage, -} from './shared.js'; - -const port = 8000 + parseInt(process.env.JEST_WORKER_ID ?? '0', 10); - -export async function mountWebSockets( - args: FixtureLoaderTestArgs, - cb: FixtureLoaderTestCallback -) { - const onMessage = jest.fn(); - - const wss = new WebSocketServer({ port }); - wss.on('connection', ws => { - ws.on('message', msg => { - onMessage(JSON.parse(msg.toString())); - }); - }); - - function getMessages() { - return onMessage.mock.calls.map( - call => (call[0] as SocketMessage).message - ); - } - - function postMessage(msg: RendererMessage) { - wss.clients.forEach(client => - client.send(JSON.stringify(rendererSocketMessage(msg))) - ); - } - - async function cleanup() { - wss.clients.forEach(client => client.close()); - wss.close(); - await waitFor(() => expect(wss.clients.size).toBe(0)); - } - - expect.hasAssertions(); - const renderer = create(getElement(args)); - try { - await waitFor(() => expect(wss.clients.size).toBe(1)); - await cb({ - renderer, - update: newArgs => renderer.update(getElement(newArgs)), - ...createRendererConnectMockApi({ getMessages, postMessage }), - }); - } finally { - renderer.unmount(); - await cleanup(); - } -} - -function getElement({ decorators = {}, ...otherArgs }: FixtureLoaderTestArgs) { - return ( - - ); -} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/wrapFixture.ts b/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/wrapFixture.ts deleted file mode 100644 index ba41befc07..0000000000 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/wrapFixture.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - ReactFixtureExport, - ReactFixtureExports, - ReactFixtureWrapper, - ReactFixtureWrappers, -} from '../../reactTypes.js'; - -export function wrapFixtures( - fixtureExports: ReactFixtureExports -): ReactFixtureWrappers { - return Object.keys(fixtureExports).reduce((acc, fixturePath) => { - return { ...acc, [fixturePath]: wrapFixture(fixtureExports[fixturePath]) }; - }, {}); -} - -function wrapFixture(fixtureExport: ReactFixtureExport): ReactFixtureWrapper { - return { module: { default: fixtureExport } }; -} diff --git a/packages/react-cosmos-core/src/renderer/FixtureStateChangeResponse.tsx b/packages/react-cosmos-core/src/renderer/FixtureStateChangeResponse.tsx new file mode 100644 index 0000000000..4dd8bba3aa --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/FixtureStateChangeResponse.tsx @@ -0,0 +1,49 @@ +import { isEqual } from 'lodash-es'; +import { Dispatch, SetStateAction, useEffect } from 'react'; +import { RendererConnect } from './rendererConnectTypes.js'; +import { SelectedFixture } from './useSelectedFixture.js'; + +type Props = { + rendererId: string; + rendererConnect: RendererConnect; + selectedFixture: SelectedFixture; + setSelectedFixture: Dispatch>; +}; +export function FixtureStateChangeResponse({ + rendererId, + rendererConnect, + selectedFixture, + setSelectedFixture, +}: Props) { + const { fixtureId, fixtureState, syncedFixtureState } = selectedFixture; + + useEffect(() => { + if (!isEqual(fixtureState, syncedFixtureState)) { + rendererConnect.postMessage({ + type: 'fixtureStateChange', + payload: { + rendererId, + fixtureId, + fixtureState, + }, + }); + setSelectedFixture(prev => { + // Ensure fixture state applies to currently selected fixture + if (prev && isEqual(prev.fixtureId, fixtureId)) { + return { ...prev, syncedFixtureState: fixtureState }; + } else { + return prev; + } + }); + } + }, [ + fixtureId, + fixtureState, + rendererConnect, + rendererId, + setSelectedFixture, + syncedFixtureState, + ]); + + return null; +} diff --git a/packages/react-cosmos-core/src/renderer/LazyFixtureListItemUpdate.tsx b/packages/react-cosmos-core/src/renderer/LazyFixtureListItemUpdate.tsx new file mode 100644 index 0000000000..cf16ad3fa2 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/LazyFixtureListItemUpdate.tsx @@ -0,0 +1,30 @@ +import { useEffect } from 'react'; +import { getFixtureItemFromExport } from './getFixtureList.js'; +import { RendererConnect } from './rendererConnectTypes.js'; +import { ReactFixtureModule } from './userModuleTypes.js'; + +type Props = { + rendererId: string; + rendererConnect: RendererConnect; + fixturePath: string; + fixtureModule: ReactFixtureModule; +}; +export function LazyFixtureListItemUpdate({ + rendererId, + rendererConnect, + fixturePath, + fixtureModule, +}: Props) { + useEffect(() => { + rendererConnect.postMessage({ + type: 'fixtureListItemUpdate', + payload: { + rendererId, + fixturePath, + fixtureItem: getFixtureItemFromExport(fixtureModule.default), + }, + }); + }, [fixtureModule.default, fixturePath, rendererConnect, rendererId]); + + return null; +} diff --git a/packages/react-cosmos-core/src/renderer/LazyFixtureLoader.tsx b/packages/react-cosmos-core/src/renderer/LazyFixtureLoader.tsx new file mode 100644 index 0000000000..b5580125fa --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/LazyFixtureLoader.tsx @@ -0,0 +1,85 @@ +import React, { ReactElement, useMemo } from 'react'; +import { FixtureId } from '../fixture/types.js'; +import { FixtureState, SetFixtureState } from '../fixtureState/types.js'; +import { DecoratedFixture } from './DecoratedFixture.js'; +import { getFixture } from './getFixture.js'; +import { getSortedDecoratorsForFixturePath } from './getSortedDecoratorsForFixturePath.js'; +import { LazyFixtureListItemUpdate } from './LazyFixtureListItemUpdate.js'; +import { RendererConnect } from './rendererConnectTypes.js'; +import { useLazyFixtureModules } from './useLazyFixtureModules.js'; +import { + ByPath, + LazyReactDecoratorWrapper, + LazyReactFixtureWrapper, + ReactDecorator, +} from './userModuleTypes.js'; + +type Props = { + rendererId: string; + rendererConnect: RendererConnect; + fixtureWrapper: LazyReactFixtureWrapper; + decorators: ByPath; + systemDecorators: ReactDecorator[]; + fixtureId: FixtureId; + fixtureState: FixtureState; + setFixtureState: SetFixtureState; + renderMessage: (msg: string) => ReactElement; + renderKey: number; + onErrorReset?: () => unknown; +}; +export function LazyFixtureLoader({ + rendererId, + rendererConnect, + fixtureWrapper, + decorators, + systemDecorators, + fixtureId, + fixtureState, + setFixtureState, + renderMessage, + renderKey, + onErrorReset, +}: Props) { + const decoratorWrappers = useMemo( + () => getSortedDecoratorsForFixturePath(fixtureId.path, decorators), + [decorators, fixtureId.path] + ); + + const modules = useLazyFixtureModules( + fixtureId.path, + fixtureWrapper, + decoratorWrappers + ); + + if (!modules) { + return null; + } + + const { fixtureModule, decoratorModules } = modules; + const fixtureExport = fixtureModule.default; + const fixture = getFixture(fixtureExport, fixtureId.name); + + if (typeof fixture === 'undefined') { + return renderMessage(`Invalid fixture ID: ${JSON.stringify(fixtureId)}`); + } + + return ( + <> + + + + ); +} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/childrenTransition.tsx b/packages/react-cosmos-core/src/renderer/__tests__/childrenTransition.tsx similarity index 85% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/childrenTransition.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/childrenTransition.tsx index 011c6f5a57..758876fd17 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/childrenTransition.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/childrenTransition.tsx @@ -1,25 +1,22 @@ import React from 'react'; -import { - createValue, - createValues, -} from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValue, createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { Wrapper } from '../testHelpers/components.js'; import { anyProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: yo, }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'transitions string children into an element with children', { rendererId, fixtures }, async ({ update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -33,7 +30,7 @@ testFixtureLoader( }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: ( brah @@ -63,11 +60,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'transitions string children into an element with children', { rendererId, fixtures }, async ({ update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -81,7 +78,7 @@ testFixtureLoader( }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: ( brah @@ -111,11 +108,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'transitions string children into an element with multiple children', { rendererId, fixtures }, async ({ update, selectFixture, fixtureStateChange }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, @@ -133,7 +130,7 @@ testFixtureLoader( }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: ( brah diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateFixtureReselectMocked.tsx b/packages/react-cosmos-core/src/renderer/__tests__/classStateFixtureReselectMocked.tsx similarity index 74% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateFixtureReselectMocked.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/classStateFixtureReselectMocked.tsx index 4e4fd39773..c047e6b4f7 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateFixtureReselectMocked.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/classStateFixtureReselectMocked.tsx @@ -1,14 +1,14 @@ import { StateMock } from '@react-mock/state'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { Counter } from '../testHelpers/components.js'; import { anyClassState, anyProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( @@ -24,12 +24,12 @@ const fixtureId = { path: 'first' }; // FixtureCapture remounted. This was related to the refactor of // FixtureCapture/attachChildRefs in // https://github.com/react-cosmos/react-cosmos/commit/56494b6ea10785cc3db8dda7a7fbcad62c8e1c12 -testFixtureLoader( +testRenderer( 'captures initial state after re-selecting fixture', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateFixtureReselectUnmocked.tsx b/packages/react-cosmos-core/src/renderer/__tests__/classStateFixtureReselectUnmocked.tsx similarity index 72% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateFixtureReselectUnmocked.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/classStateFixtureReselectUnmocked.tsx index 241a7a19e9..06a82064a8 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateFixtureReselectUnmocked.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/classStateFixtureReselectUnmocked.tsx @@ -1,13 +1,13 @@ import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { Counter } from '../testHelpers/components.js'; import { anyClassState, anyProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: , }); const fixtureId = { path: 'first' }; @@ -19,12 +19,12 @@ const fixtureId = { path: 'first' }; // FixtureCapture remounted. This was related to the refactor of // FixtureCapture/attachChildRefs in // https://github.com/react-cosmos/react-cosmos/commit/56494b6ea10785cc3db8dda7a7fbcad62c8e1c12 -testFixtureLoader( +testRenderer( 'captures initial state after re-selecting fixture', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMocked.tsx b/packages/react-cosmos-core/src/renderer/__tests__/classStateMocked.tsx similarity index 82% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMocked.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/classStateMocked.tsx index 1992cad2ea..95e5e92155 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMocked.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/classStateMocked.tsx @@ -4,20 +4,23 @@ import React from 'react'; import { removeFixtureStateClassState, updateFixtureStateClassState, -} from '../../../fixtureState/classState.js'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +} from '../../fixtureState/classState.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { CoolCounter, Counter } from '../testHelpers/components.js'; import { anyClassState, anyProps, getClassState, } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapActSetTimeout } from '../testHelpers/wrapActSetTimeout.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; + +beforeAll(wrapActSetTimeout); const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( @@ -26,11 +29,11 @@ const fixtures = wrapFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures mocked state', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await retry(() => expect(renderer.toJSON()).toBe('5 times')); await fixtureStateChange({ rendererId, @@ -48,14 +51,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'overwrites mocked state', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -69,7 +72,7 @@ testFixtureLoader( await retry(() => expect(renderer.toJSON()).toBe('100 times')); // A second update will provide code coverage for a different branch: // the transition between fixture state values - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -84,14 +87,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'removes mocked state property', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -106,7 +109,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'reverts to mocked state', { rendererId, fixtures }, async ({ @@ -116,10 +119,10 @@ testFixtureLoader( fixtureStateChange, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -131,7 +134,7 @@ testFixtureLoader( }, }); await retry(() => expect(renderer.toJSON()).toBe('10 times')); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -157,7 +160,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'applies fixture state to replaced component type', { rendererId, fixtures }, async ({ @@ -167,10 +170,10 @@ testFixtureLoader( setFixtureState, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -184,7 +187,7 @@ testFixtureLoader( await retry(() => expect(renderer.toJSON()).toBe('50 times')); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: ( @@ -192,11 +195,11 @@ testFixtureLoader( ), }), }); - expect(renderer.toJSON()).toBe('50 timez'); + await retry(() => expect(renderer.toJSON()).toBe('50 timez')); } ); -testFixtureLoader( +testRenderer( 'overwrites fixture state on fixture change', { rendererId, fixtures }, async ({ @@ -207,10 +210,10 @@ testFixtureLoader( fixtureStateChange, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -225,7 +228,7 @@ testFixtureLoader( // When the fixture changes the fixture state follows along update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: ( @@ -250,11 +253,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'clears fixture state for removed fixture element', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -270,13 +273,13 @@ testFixtureLoader( }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ // Counter element from fixture is gone, and so should the // fixture state related to it. first: 'No counts for you.', }), }); - expect(renderer.toJSON()).toBe('No counts for you.'); + await retry(() => expect(renderer.toJSON()).toBe('No counts for you.')); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMockedRef.tsx b/packages/react-cosmos-core/src/renderer/__tests__/classStateMockedRef.tsx similarity index 78% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMockedRef.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/classStateMockedRef.tsx index 5cb4dd2a3b..c47574f61d 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMockedRef.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/classStateMockedRef.tsx @@ -3,12 +3,15 @@ import retry from '@skidding/async-retry'; import until from 'async-until'; import delay from 'delay'; import React from 'react'; -import { FixtureStatePrimitiveValue } from '../../../fixtureState/types.js'; -import { uuid } from '../../../utils/uuid.js'; +import { FixtureStatePrimitiveValue } from '../../fixtureState/types.js'; +import { uuid } from '../../utils/uuid.js'; import { Counter } from '../testHelpers/components.js'; import { getClassState } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapActSetTimeout } from '../testHelpers/wrapActSetTimeout.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; + +beforeAll(wrapActSetTimeout); let counterRef: null | Counter = null; beforeEach(() => { @@ -17,7 +20,7 @@ beforeEach(() => { const rendererId = uuid(); const getFixtures = () => - wrapFixtures({ + wrapDefaultExport({ first: ( }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures component state changes', { rendererId, fixtures: getFixtures() }, async ({ selectFixture, getLastFixtureState }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMulti.tsx b/packages/react-cosmos-core/src/renderer/__tests__/classStateMulti.tsx similarity index 75% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMulti.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/classStateMulti.tsx index c981b0d0cc..2a23cae0b2 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateMulti.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/classStateMulti.tsx @@ -1,20 +1,20 @@ import { StateMock } from '@react-mock/state'; import retry from '@skidding/async-retry'; import React from 'react'; -import { updateFixtureStateClassState } from '../../../fixtureState/classState.js'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { updateFixtureStateClassState } from '../../fixtureState/classState.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { Counter } from '../testHelpers/components.js'; import { anyClassState, anyProps, getClassState, } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( <> @@ -28,11 +28,11 @@ const fixtures = wrapFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures mocked state from multiple instances', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -53,14 +53,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'overwrites mocked state in second instances', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [, { elementId }] = getClassState(fixtureState, 2); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateUnmocked.tsx b/packages/react-cosmos-core/src/renderer/__tests__/classStateUnmocked.tsx similarity index 77% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateUnmocked.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/classStateUnmocked.tsx index 44507618b8..d9acbe7f67 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/classStateUnmocked.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/classStateUnmocked.tsx @@ -3,29 +3,29 @@ import React from 'react'; import { removeFixtureStateClassState, updateFixtureStateClassState, -} from '../../../fixtureState/classState.js'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +} from '../../fixtureState/classState.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { Counter } from '../testHelpers/components.js'; import { anyClassState, anyProps, getClassState, } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: , }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures initial state', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await retry(() => expect(renderer.toJSON()).toBe('0 times')); await fixtureStateChange({ rendererId, @@ -43,14 +43,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'overwrites initial state', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -65,14 +65,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'removes initial state property', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -87,14 +87,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'reverts to initial state', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getClassState(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -106,7 +106,7 @@ testFixtureLoader( }, }); await retry(() => expect(renderer.toJSON()).toBe('5 times')); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/decorators.tsx b/packages/react-cosmos-core/src/renderer/__tests__/decorators.tsx similarity index 78% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/decorators.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/decorators.tsx index 23bdb6d4fa..ba2acd21df 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/decorators.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/decorators.tsx @@ -1,19 +1,19 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; type Props = { children: React.ReactNode; }; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ 'src/foo/__fixtures__/default.js': 'Hello!', }); // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051 -const decorators = { +const decorators = wrapDefaultExport({ 'src/decorator.js': ({ children }: Props) => <>Decorated at src{children}, 'src/foo/decorator.js': ({ children }: Props) => ( <>Decorated at src/foo{children} @@ -21,14 +21,14 @@ const decorators = { 'src/bar/decorator.js': ({ children }: Props) => ( <>Decorated at src/bar{children} ), -}; +}); -testFixtureLoader( +testRenderer( 'renders selected fixture inside decorator', { rendererId, fixtures, decorators }, async ({ renderer, selectFixture }) => { const [path] = Object.keys(fixtures); - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path }, fixtureState: {}, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/errorResetCallback.ts b/packages/react-cosmos-core/src/renderer/__tests__/errorResetCallback.ts similarity index 58% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/errorResetCallback.ts rename to packages/react-cosmos-core/src/renderer/__tests__/errorResetCallback.ts index 46ab93eeb9..c442a8be16 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/errorResetCallback.ts +++ b/packages/react-cosmos-core/src/renderer/__tests__/errorResetCallback.ts @@ -1,10 +1,10 @@ import retry from '@skidding/async-retry'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ first: 'First' }); +const fixtures = wrapDefaultExport({ first: 'First' }); const fixtureId = { path: 'first' }; const onErrorReset = jest.fn(); @@ -12,21 +12,21 @@ beforeEach(() => { onErrorReset.mockReset(); }); -testFixtureLoader( +testRenderer( 'fires error reset callback when selecting fixture', { rendererId, fixtures, onErrorReset }, async ({ selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await retry(() => expect(onErrorReset).toBeCalledTimes(1)); } ); -testFixtureLoader( +testRenderer( 'fires error reset callback when unselecting fixture', { rendererId, fixtures, onErrorReset }, async ({ selectFixture, unselectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); - await unselectFixture({ rendererId }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); + unselectFixture({ rendererId }); await retry(() => expect(onErrorReset).toBeCalledTimes(2)); } ); diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureAutoSelect.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureAutoSelect.ts similarity index 69% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureAutoSelect.ts rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureAutoSelect.ts index 6b46e63dc5..4d975f2b24 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureAutoSelect.ts +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureAutoSelect.ts @@ -1,15 +1,15 @@ import retry from '@skidding/async-retry'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: { one: 'First' }, second: 'Second', }); -testFixtureLoader( +testRenderer( 'renders auto selected named fixture', { rendererId, fixtures, selectedFixtureId: { path: 'first', name: 'one' } }, async ({ renderer }) => { @@ -17,7 +17,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'renders auto selected unnamed fixture', { rendererId, fixtures, selectedFixtureId: { path: 'second' } }, async ({ renderer }) => { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureInitialSelect.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelect.ts similarity index 78% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureInitialSelect.ts rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelect.ts index 75d7e8f87c..9d02d1ce09 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureInitialSelect.ts +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelect.ts @@ -1,15 +1,15 @@ import retry from '@skidding/async-retry'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: { one: 'First' }, second: 'Second', }); -testFixtureLoader( +testRenderer( 'renders initially selected named fixture', { rendererId, fixtures, initialFixtureId: { path: 'first', name: 'one' } }, async ({ renderer }) => { @@ -17,7 +17,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'renders initially selected unnamed fixture', { rendererId, fixtures, initialFixtureId: { path: 'second' } }, async ({ renderer }) => { @@ -25,7 +25,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'posts ready response on mount with initialFixtureId', { rendererId, fixtures, initialFixtureId: { path: 'second' } }, async ({ rendererReady }) => { diff --git a/packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelectLazy.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelectLazy.ts new file mode 100644 index 0000000000..96aa92a1d6 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureInitialSelectLazy.ts @@ -0,0 +1,51 @@ +import retry from '@skidding/async-retry'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; + +const rendererId = uuid(); +const fixtures = wrapDefaultExport({ + first: { one: 'First' }, + second: 'Second', +}); + +testRenderer( + 'renders lazy initially selected named fixture', + { rendererId, fixtures, initialFixtureId: { path: 'first', name: 'one' } }, + async ({ renderer }) => { + await retry(() => expect(renderer.toJSON()).toBe('First')); + } +); + +testRenderer( + 'renders lazy initially selected unnamed fixture', + { rendererId, fixtures, initialFixtureId: { path: 'second' } }, + async ({ renderer }) => { + await retry(() => expect(renderer.toJSON()).toBe('Second')); + } +); + +testRenderer( + 'posts lazy ready response and fixture list item update on mount with initialFixtureId', + { + rendererId, + fixtures, + initialFixtureId: { path: 'first' }, + lazy: true, + }, + async ({ rendererReady, fixtureListItemUpdate }) => { + await rendererReady({ + rendererId, + fixtures: { + first: { type: 'single' }, + second: { type: 'single' }, + }, + initialFixtureId: { path: 'first' }, + }); + await fixtureListItemUpdate({ + rendererId, + fixturePath: 'first', + fixtureItem: { type: 'multi', fixtureNames: ['one'] }, + }); + } +); diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureList.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureList.ts similarity index 77% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureList.ts rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureList.ts index 6990f554ff..1e8f00408c 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureList.ts +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureList.ts @@ -1,11 +1,11 @@ -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ first: null, second: null }); +const fixtures = wrapDefaultExport({ first: null, second: null }); -testFixtureLoader( +testRenderer( 'renders blank state message', { rendererId, fixtures }, async ({ renderer }) => { @@ -13,7 +13,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'posts ready response on mount', { rendererId, fixtures }, async ({ rendererReady }) => { @@ -27,7 +27,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'posts ready response again on ping request', { rendererId, fixtures }, async ({ rendererReady, pingRenderers }) => { @@ -38,7 +38,7 @@ testFixtureLoader( second: { type: 'single' }, }, }); - await pingRenderers(); + pingRenderers(); await rendererReady({ rendererId, fixtures: { @@ -49,7 +49,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'posts fixture list on "fixtures" prop change', { rendererId, fixtures }, async ({ update, rendererReady, fixtureListUpdate }) => { @@ -62,7 +62,7 @@ testFixtureLoader( }); update({ rendererId, - fixtures: { ...fixtures, ...wrapFixtures({ third: null }) }, + fixtures: { ...fixtures, ...wrapDefaultExport({ third: null }) }, }); await fixtureListUpdate({ rendererId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureListMulti.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureListMulti.ts similarity index 61% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureListMulti.ts rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureListMulti.ts index c04d1bf480..a4df03e2b2 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureListMulti.ts +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureListMulti.ts @@ -1,14 +1,14 @@ -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: { a: null, b: null, c: null }, second: null, }); -testFixtureLoader( +testRenderer( 'posts ready response on mount', { rendererId, fixtures }, async ({ rendererReady }) => { diff --git a/packages/react-cosmos-core/src/renderer/__tests__/fixtureListMultiLazy.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureListMultiLazy.ts new file mode 100644 index 0000000000..129565e8d7 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureListMultiLazy.ts @@ -0,0 +1,40 @@ +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; + +const rendererId = uuid(); +const fixtures = wrapDefaultExport({ + first: { a: null, b: null, c: null }, + second: null, +}); + +testRenderer( + 'posts lazy ready response on mount without fixture names', + { rendererId, fixtures, lazy: true }, + async ({ rendererReady }) => { + await rendererReady({ + rendererId, + fixtures: { + first: { type: 'single' }, + second: { type: 'single' }, + }, + }); + } +); + +testRenderer( + 'posts lazy ready response on mount without fixture names', + { rendererId, fixtures, lazy: true }, + async ({ selectFixture, fixtureListItemUpdate }) => { + selectFixture({ + rendererId, + fixtureId: { path: 'first' }, + fixtureState: {}, + }); + await fixtureListItemUpdate({ + rendererId, + fixturePath: 'first', + fixtureItem: { type: 'multi', fixtureNames: ['a', 'b', 'c'] }, + }); + } +); diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureSelect.ts b/packages/react-cosmos-core/src/renderer/__tests__/fixtureSelect.ts similarity index 72% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureSelect.ts rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureSelect.ts index 74bcc41f1a..d2ebc7cb71 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureSelect.ts +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureSelect.ts @@ -1,19 +1,19 @@ import retry from '@skidding/async-retry'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: { one: 'First' }, second: 'Second', }); -testFixtureLoader( +testRenderer( 'renders selected fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path: 'second' }, fixtureState: {}, @@ -22,11 +22,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'renders selected named fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path: 'first', name: 'one' }, fixtureState: {}, @@ -35,11 +35,24 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( + 'renders first named fixture', + { rendererId, fixtures }, + async ({ renderer, selectFixture }) => { + selectFixture({ + rendererId, + fixtureId: { path: 'first' }, + fixtureState: {}, + }); + await retry(() => expect(renderer.toJSON()).toBe('First')); + } +); + +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path: 'second' }, fixtureState: {}, @@ -54,26 +67,26 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'renders blank state after unselecting fixture', { rendererId, fixtures }, async ({ renderer, selectFixture, unselectFixture }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path: 'first', name: 'one' }, fixtureState: {}, }); await retry(() => expect(renderer.toJSON()).toBe('First')); - await unselectFixture({ rendererId }); + unselectFixture({ rendererId }); await retry(() => expect(renderer.toJSON()).toBe('No fixture selected.')); } ); -testFixtureLoader( +testRenderer( 'ignores "selectFixture" message for different renderer', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ + selectFixture({ rendererId: 'foobar', fixtureId: { path: 'second' }, fixtureState: {}, @@ -82,11 +95,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'renders missing state on unknown fixture path', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path: 'third' }, fixtureState: {}, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureSelectWithState.tsx b/packages/react-cosmos-core/src/renderer/__tests__/fixtureSelectWithState.tsx similarity index 61% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureSelectWithState.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureSelectWithState.tsx index dc86c36e0e..586398a5e8 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureSelectWithState.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureSelectWithState.tsx @@ -1,23 +1,23 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { createFixtureStateProps } from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { createFixtureStateProps } from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { HelloMessage } from '../testHelpers/components.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ - first: , +const fixtures = wrapDefaultExport({ + first: , }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders selected fixture with fixture state', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureStateChange.tsx b/packages/react-cosmos-core/src/renderer/__tests__/fixtureStateChange.tsx similarity index 75% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureStateChange.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/fixtureStateChange.tsx index 6aa1fd63df..22aa5f8eb4 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fixtureStateChange.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/fixtureStateChange.tsx @@ -1,10 +1,10 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer } from 'react-test-renderer'; -import { FixtureContext } from '../../../fixture/FixtureContext.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { act, ReactTestRenderer } from 'react-test-renderer'; +import { FixtureContext } from '../../fixture/FixtureContext.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; function MyComponent() { const { setFixtureState } = React.useContext(FixtureContext); @@ -26,15 +26,15 @@ function MyComponent() { } const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: MyComponent, }); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId: { path: 'first' }, fixtureState: { props: [] }, @@ -60,5 +60,7 @@ testFixtureLoader( ); function clickButtonByLabel(renderer: ReactTestRenderer, label: string) { - renderer.root.findByProps({ children: label }).props.onClick(); + act(() => { + renderer.root.findByProps({ children: label }).props.onClick(); + }); } diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fragmentTransition.tsx b/packages/react-cosmos-core/src/renderer/__tests__/fragmentTransition.tsx similarity index 82% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fragmentTransition.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/fragmentTransition.tsx index 46652db541..c61f2af561 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/fragmentTransition.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/fragmentTransition.tsx @@ -1,14 +1,14 @@ import { StateMock } from '@react-mock/state'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { Counter } from '../testHelpers/components.js'; import { anyClassState, anyProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( <> @@ -19,14 +19,14 @@ const fixtures = wrapFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'transitions Fragment from single to multi children', { rendererId, fixtures }, async ({ update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ // This is a very tricky case. When fragments have one child, // props.children will be that child. But when fragments have // two or more children, props.children will be an array. When diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/props.tsx b/packages/react-cosmos-core/src/renderer/__tests__/props.tsx similarity index 73% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/props.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/props.tsx index f86bd2625d..34cc20bd6f 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/props.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/props.tsx @@ -1,28 +1,28 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; +import { createValues } from '../../fixtureState/createValues.js'; import { removeFixtureStateProps, updateFixtureStateProps, -} from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +} from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { HelloMessage } from '../testHelpers/components.js'; import { anyProps, getProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ - first: , +const fixtures = wrapDefaultExport({ + first: , }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures props', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); - await retry(() => expect(renderer.toJSON()).toBe('Hello Bianca')); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); + await retry(() => expect(renderer.toJSON()).toBe('Hello Blanca')); await fixtureStateChange({ rendererId, fixtureId, @@ -30,7 +30,7 @@ testFixtureLoader( props: [ anyProps({ componentName: 'HelloMessage', - values: createValues({ name: 'Bianca' }), + values: createValues({ name: 'Blanca' }), }), ], }, @@ -38,14 +38,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'overwrites prop', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -60,14 +60,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'removes prop', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -82,7 +82,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'clears props', { rendererId, fixtures }, async ({ @@ -92,10 +92,10 @@ testFixtureLoader( fixtureStateChange, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -107,14 +107,14 @@ testFixtureLoader( }, }); await retry(() => expect(renderer.toJSON()).toBe('Hello B')); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { props: removeFixtureStateProps(fixtureState, elementId), }, }); - await retry(() => expect(renderer.toJSON()).toBe('Hello Bianca')); + await retry(() => expect(renderer.toJSON()).toBe('Hello Blanca')); // After the props are removed from the fixture state, the original // props are added back through a fixtureStateChange message await fixtureStateChange({ @@ -124,7 +124,7 @@ testFixtureLoader( props: [ anyProps({ componentName: 'HelloMessage', - values: createValues({ name: 'Bianca' }), + values: createValues({ name: 'Blanca' }), }), ], }, @@ -132,7 +132,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'overwrites fixture state on fixture change', { rendererId, fixtures }, async ({ @@ -143,10 +143,10 @@ testFixtureLoader( fixtureStateChange, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -160,8 +160,8 @@ testFixtureLoader( await retry(() => expect(renderer.toJSON()).toBe('Hello B')); update({ rendererId, - fixtures: wrapFixtures({ - first: , + fixtures: wrapDefaultExport({ + first: , }), }); await fixtureStateChange({ @@ -171,20 +171,20 @@ testFixtureLoader( props: [ anyProps({ componentName: 'HelloMessage', - values: createValues({ name: 'Petec' }), + values: createValues({ name: 'Benjamin' }), }), ], }, }); - await retry(() => expect(renderer.toJSON()).toBe('Hello Petec')); + await retry(() => expect(renderer.toJSON()).toBe('Hello Benjamin')); } ); -testFixtureLoader( +testRenderer( 'clears fixture state for removed fixture element', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -192,20 +192,20 @@ testFixtureLoader( props: [ anyProps({ componentName: 'HelloMessage', - values: createValues({ name: 'Bianca' }), + values: createValues({ name: 'Blanca' }), }), ], }, }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ // HelloMessage element from fixture is gone, and so should the // fixture state related to it. first: 'Hello all', }), }); - expect(renderer.toJSON()).toBe('Hello all'); + await retry(() => expect(renderer.toJSON()).toBe('Hello all')); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsAndState.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsAndState.tsx similarity index 76% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsAndState.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsAndState.tsx index 966bf6bb51..54fea8be7a 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsAndState.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsAndState.tsx @@ -1,20 +1,20 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { updateFixtureStateClassState } from '../../../fixtureState/classState.js'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { updateFixtureStateProps } from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +import { updateFixtureStateClassState } from '../../fixtureState/classState.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { updateFixtureStateProps } from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { SuffixCounter } from '../testHelpers/components.js'; import { anyClassState, anyProps, getProps, } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( // The extra levels of nesting capture a complex case regarding deep // comparison of children nodes @@ -27,11 +27,11 @@ const fixtures = wrapFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'keeps state when resetting props', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); let fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); fixtureState = { @@ -42,9 +42,9 @@ testFixtureLoader( values: createValues({ count: 5 }), }), }; - await setFixtureState({ rendererId, fixtureId, fixtureState }); + setFixtureState({ rendererId, fixtureId, fixtureState }); await retry(() => expect(renderer.toJSON()).toBe('5 times')); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -60,11 +60,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'keeps state when transitioning props', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); let fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); fixtureState = { @@ -75,9 +75,9 @@ testFixtureLoader( values: createValues({ count: 5 }), }), }; - await setFixtureState({ rendererId, fixtureId, fixtureState }); + setFixtureState({ rendererId, fixtureId, fixtureState }); await retry(() => expect(renderer.toJSON()).toBe('5 times')); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -93,11 +93,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'keeps props when changing state', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); let fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); fixtureState = { @@ -108,9 +108,9 @@ testFixtureLoader( values: createValues({ suffix: 'timez' }), }), }; - await setFixtureState({ rendererId, fixtureId, fixtureState }); + setFixtureState({ rendererId, fixtureId, fixtureState }); await retry(() => expect(renderer.toJSON()).toBe('0 timez')); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -126,14 +126,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates props on fixture change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: , }), }); diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsAndStateRef.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsAndStateRef.tsx similarity index 76% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsAndStateRef.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsAndStateRef.tsx index 5c9c31f0de..47bcbcf4bc 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsAndStateRef.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsAndStateRef.tsx @@ -1,17 +1,20 @@ import retry from '@skidding/async-retry'; import until from 'async-until'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { updateFixtureStateProps } from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { updateFixtureStateProps } from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { SuffixCounter } from '../testHelpers/components.js'; import { anyClassState, anyProps, getProps, } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapActSetTimeout } from '../testHelpers/wrapActSetTimeout.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; + +beforeAll(wrapActSetTimeout); let counterRef: null | SuffixCounter = null; beforeEach(() => { @@ -20,7 +23,7 @@ beforeEach(() => { const rendererId = uuid(); const getFixtures = () => - wrapFixtures({ + wrapDefaultExport({ first: ( { @@ -34,7 +37,7 @@ const getFixtures = () => }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'keeps props when state changes', { rendererId, fixtures: getFixtures() }, async ({ @@ -44,10 +47,10 @@ testFixtureLoader( fixtureStateChange, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsFnChildren.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsFnChildren.tsx similarity index 69% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsFnChildren.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsFnChildren.tsx index f3086563b1..b578b9254f 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsFnChildren.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsFnChildren.tsx @@ -1,12 +1,12 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { FixtureCapture } from '../FixtureCapture/index.js'; import { HelloMessage } from '../testHelpers/components.js'; import { anyProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; function Wrap({ children }: { children: () => React.ReactNode }) { return <>{children()}; @@ -14,10 +14,10 @@ function Wrap({ children }: { children: () => React.ReactNode }) { Wrap.cosmosCapture = false; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( <> - {() => } + {() => } {() => ( @@ -30,13 +30,13 @@ const fixtures = wrapFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures props from render callback', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await retry(() => - expect(renderer.toJSON()).toEqual(['Hello Bianca', 'Hello B']) + expect(renderer.toJSON()).toEqual(['Hello Blanca', 'Hello B']) ); await fixtureStateChange({ rendererId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsMulti.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsMulti.tsx similarity index 67% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsMulti.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsMulti.tsx index 3157a210e0..f7ec8edf76 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsMulti.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsMulti.tsx @@ -1,35 +1,35 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { updateFixtureStateProps } from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { updateFixtureStateProps } from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { HelloMessage } from '../testHelpers/components.js'; import { anyProps, getProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( <> - + ), }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'captures multiple props instances', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, }); await retry(() => - expect(renderer.toJSON()).toEqual(['Hello Bianca', 'Hello B']) + expect(renderer.toJSON()).toEqual(['Hello Blanca', 'Hello B']) ); await fixtureStateChange({ rendererId, @@ -39,7 +39,7 @@ testFixtureLoader( anyProps({ componentName: 'HelloMessage', elPath: 'props.children[0]', - values: createValues({ name: 'Bianca' }), + values: createValues({ name: 'Blanca' }), }), anyProps({ componentName: 'HelloMessage', @@ -52,30 +52,30 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'overwrites prop in second instance', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, }); const fixtureState = await getLastFixtureState(); const [, { elementId }] = getProps(fixtureState, 2); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { props: updateFixtureStateProps({ fixtureState, elementId, - values: createValues({ name: 'Petec' }), + values: createValues({ name: 'Benjamin' }), }), }, }); await retry(() => - expect(renderer.toJSON()).toEqual(['Hello Bianca', 'Hello Petec']) + expect(renderer.toJSON()).toEqual(['Hello Blanca', 'Hello Benjamin']) ); } ); diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsObjectIdentity.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsObjectIdentity.tsx similarity index 73% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsObjectIdentity.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsObjectIdentity.tsx index b53ef60cc3..b998b8bd70 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsObjectIdentity.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsObjectIdentity.tsx @@ -1,11 +1,11 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { updateFixtureStateProps } from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { updateFixtureStateProps } from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { getProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); const fixtureId = { path: 'first' }; @@ -21,40 +21,34 @@ function TestComponent({ obj, cb }: Props) { } function createFixtures(obj: {}, cb: (newObj: {}) => unknown) { - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } -testFixtureLoader( +testRenderer( 'preserves object reference', { rendererId, fixtures: {} }, async ({ update, selectFixture }) => { const obj = {}; const cb = jest.fn(); update({ rendererId, fixtures: createFixtures(obj, cb) }); - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await retry(() => expect(getLastMockCall(cb)[0]).toBe(obj)); } ); -testFixtureLoader( +testRenderer( 'preserves updated object reference', { rendererId, fixtures: {} }, - async ({ - renderer, - update, - selectFixture, - getLastFixtureState, - setFixtureState, - }) => { + async ({ update, selectFixture, getLastFixtureState, setFixtureState }) => { const obj = {}; const cb = jest.fn(); update({ rendererId, fixtures: createFixtures(obj, cb) }); - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsRef.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsRef.tsx similarity index 76% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsRef.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsRef.tsx index 5204fc2c13..edb80c085a 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsRef.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsRef.tsx @@ -1,16 +1,16 @@ import retry from '@skidding/async-retry'; import { uniq } from 'lodash-es'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; +import { createValues } from '../../fixtureState/createValues.js'; import { resetFixtureStateProps, updateFixtureStateProps, -} from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +} from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { HelloMessageCls } from '../testHelpers/components.js'; import { getProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); const fixtureId = { path: 'first' }; @@ -23,7 +23,7 @@ beforeEach(() => { // Intentionally create new ref function on every update to get the ref // to be called more than once even if the component instance is reused const getFixtures = () => - wrapFixtures({ + wrapDefaultExport({ first: ( { @@ -31,12 +31,12 @@ const getFixtures = () => refs.push(elRef); } }} - name="Bianca" + name="Blanca" /> ), }); -testFixtureLoader( +testRenderer( 'transitions props (reuses component instance)', { rendererId, fixtures: getFixtures() }, async ({ @@ -46,10 +46,10 @@ testFixtureLoader( setFixtureState, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -63,13 +63,13 @@ testFixtureLoader( await retry(() => expect(renderer.toJSON()).toEqual('Hello B')); update({ rendererId, fixtures: getFixtures() }); await retry(() => { - expect(renderer.toJSON()).toEqual('Hello Bianca'); + expect(renderer.toJSON()).toEqual('Hello Blanca'); expect(uniq(refs).length).toBe(1); }); } ); -testFixtureLoader( +testRenderer( 'resets props (creates new component instance)', { rendererId, fixtures: getFixtures() }, async ({ @@ -79,10 +79,10 @@ testFixtureLoader( setFixtureState, getLastFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -96,7 +96,7 @@ testFixtureLoader( await retry(() => expect(renderer.toJSON()).toEqual('Hello B')); update({ rendererId, fixtures: getFixtures() }); await retry(() => { - expect(renderer.toJSON()).toEqual('Hello Bianca'); + expect(renderer.toJSON()).toEqual('Hello Blanca'); expect(uniq(refs).length).toBe(2); }); } diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsTypeChange.tsx b/packages/react-cosmos-core/src/renderer/__tests__/propsTypeChange.tsx similarity index 73% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsTypeChange.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/propsTypeChange.tsx index 0108f7891d..3dcd1440d5 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/propsTypeChange.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/propsTypeChange.tsx @@ -1,11 +1,11 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { updateFixtureStateProps } from '../../../fixtureState/props.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { updateFixtureStateProps } from '../../fixtureState/props.js'; +import { uuid } from '../../utils/uuid.js'; import { getProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); const fixtures = createFixtures(); @@ -15,12 +15,12 @@ function createFixtures() { function HelloMessage({ name }: { name: string }) { return <>{`Hello ${name}`}; } - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } -testFixtureLoader( +testRenderer( 'persists props after type changes reference but keeps name (hmr simulation)', { rendererId, fixtures }, async ({ @@ -30,11 +30,11 @@ testFixtureLoader( getLastFixtureState, setFixtureState, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await retry(() => expect(renderer.toJSON()).toBe('Hello Theo')); const fixtureState = await getLastFixtureState(); const [{ elementId }] = getProps(fixtureState); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/selects.tsx b/packages/react-cosmos-core/src/renderer/__tests__/selects.tsx similarity index 80% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/selects.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/selects.tsx index 74194f345c..780150ac2e 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/selects.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/selects.tsx @@ -1,10 +1,14 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useSelect } from '../../../fixture/useSelect/index.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { + act, + ReactTestRenderer, + ReactTestRendererJSON, +} from 'react-test-renderer'; +import { useSelect } from '../../fixture/useSelect/index.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; type Option = 'first' | 'second' | 'third'; @@ -24,7 +28,7 @@ function createFixtures({ defaultValue }: { defaultValue: Option }) { /> ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -33,20 +37,20 @@ const rendererId = uuid(); const fixtures = createFixtures({ defaultValue: 'first' }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'first'); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -65,14 +69,14 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'reflects fixture state change', { rendererId, fixtures }, async ({ renderer, selectFixture, setFixtureState, getLastFixtureState }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'first'); const fixtureState = await getLastFixtureState(); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -92,11 +96,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates fixture state via setter', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'first'); changeValue(renderer, 'second'); await rendered(renderer, 'second'); @@ -118,11 +122,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'resets fixture state on default value change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'first'); update({ rendererId, @@ -154,7 +158,9 @@ async function rendered(renderer: ReactTestRenderer, text: string) { } function changeValue(renderer: ReactTestRenderer, value: Option) { - getSingleRendererElement(renderer).props.onChange({ target: { value } }); + act(() => { + getSingleRendererElement(renderer).props.onChange({ target: { value } }); + }); } function getSingleRendererElement(renderer: ReactTestRenderer) { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/selectsNoDefault.tsx b/packages/react-cosmos-core/src/renderer/__tests__/selectsNoDefault.tsx similarity index 77% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/selectsNoDefault.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/selectsNoDefault.tsx index e979a8c78c..968cfb5711 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/selectsNoDefault.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/selectsNoDefault.tsx @@ -1,10 +1,10 @@ import retry from '@skidding/async-retry'; import React from 'react'; import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useSelect } from '../../../fixture/useSelect/index.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { useSelect } from '../../fixture/useSelect/index.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; function createFixtures() { const MyComponent = () => { @@ -19,7 +19,7 @@ function createFixtures() { /> ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -28,20 +28,20 @@ const rendererId = uuid(); const fixtures = createFixtures(); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'first'); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/typeFunction.tsx b/packages/react-cosmos-core/src/renderer/__tests__/typeFunction.tsx similarity index 56% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/typeFunction.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/typeFunction.tsx index 78dfc0973e..4611b75e31 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/typeFunction.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/typeFunction.tsx @@ -1,19 +1,19 @@ import React from 'react'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: () => , }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'collects no props fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/typeString.tsx b/packages/react-cosmos-core/src/renderer/__tests__/typeString.tsx similarity index 58% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/typeString.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/typeString.tsx index c17e8cb564..6be008a86a 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/typeString.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/typeString.tsx @@ -1,18 +1,21 @@ import React from 'react'; -import { createValues } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; +import { createValues } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; import { anyProps } from '../testHelpers/fixtureState.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'collects fixture state for interesting string element type', - { rendererId, fixtures: wrapFixtures({ first: }) }, + { + rendererId, + fixtures: wrapDefaultExport({ first: }), + }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -28,11 +31,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'collects no fixture state for uninteresting string element type', - { rendererId, fixtures: wrapFixtures({ first:
yo
}) }, + { rendererId, fixtures: wrapDefaultExport({ first:
yo
}) }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesArray.tsx b/packages/react-cosmos-core/src/renderer/__tests__/valuesArray.tsx similarity index 83% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesArray.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/valuesArray.tsx index 077fe84f13..ebdbdbe8bb 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesArray.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/valuesArray.tsx @@ -1,11 +1,11 @@ import retry from '@skidding/async-retry'; import React from 'react'; import { ReactTestRenderer } from 'react-test-renderer'; -import { useValue } from '../../../fixture/useValue/index.js'; -import { createValue } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { useValue } from '../../fixture/useValue/index.js'; +import { createValue } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; type Profile = { isAdmin: boolean; @@ -19,7 +19,7 @@ function createFixtures({ defaultValue }: { defaultValue: Profile[] }) { const [profiles] = useValue('profiles', { defaultValue }); return <>{JSON.stringify(profiles, null, 2)}; }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -30,20 +30,20 @@ const fixtures = createFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, [{ isAdmin: true, name: 'Pat D', age: 45 }]); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -65,11 +65,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'resets fixture state on default value change', { rendererId, fixtures }, async ({ update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); update({ rendererId, fixtures: createFixtures({ diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesBoolean.tsx b/packages/react-cosmos-core/src/renderer/__tests__/valuesBoolean.tsx similarity index 76% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesBoolean.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/valuesBoolean.tsx index ebb95b230b..c3cc7b821e 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesBoolean.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/valuesBoolean.tsx @@ -1,11 +1,15 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useValue } from '../../../fixture/useValue/index.js'; -import { createValue } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { + act, + ReactTestRenderer, + ReactTestRendererJSON, +} from 'react-test-renderer'; +import { useValue } from '../../fixture/useValue/index.js'; +import { createValue } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; function createFixtures({ defaultValue }: { defaultValue: boolean }) { const MyComponent = () => { @@ -14,7 +18,7 @@ function createFixtures({ defaultValue }: { defaultValue: boolean }) { ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -23,20 +27,20 @@ const rendererId = uuid(); const fixtures = createFixtures({ defaultValue: false }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'false'); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -54,11 +58,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates fixture state via setter', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'false'); toggleButton(renderer); await fixtureStateChange({ @@ -78,11 +82,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'resets fixture state on default value change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'false'); update({ rendererId, @@ -114,7 +118,9 @@ async function rendered(renderer: ReactTestRenderer, text: string) { } function toggleButton(renderer: ReactTestRenderer) { - getSingleRendererElement(renderer).props.onClick(); + act(() => { + getSingleRendererElement(renderer).props.onClick(); + }); } function getSingleRendererElement(renderer: ReactTestRenderer) { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesHotReload.tsx b/packages/react-cosmos-core/src/renderer/__tests__/valuesHotReload.tsx similarity index 89% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesHotReload.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/valuesHotReload.tsx index 301c8b4f6a..1220a60526 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesHotReload.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/valuesHotReload.tsx @@ -1,11 +1,15 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useValue } from '../../../fixture/useValue/index.js'; -import { createValue } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { + act, + ReactTestRenderer, + ReactTestRendererJSON, +} from 'react-test-renderer'; +import { useValue } from '../../fixture/useValue/index.js'; +import { createValue } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; type CreateFixtureArgs = { countName?: string; @@ -36,7 +40,7 @@ function createFixtures({ ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -45,11 +49,11 @@ const rendererId = uuid(); const fixtures = createFixtures(); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'preserves fixture state change (via setter) on default value change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, { countText: '0', toggledText: 'false' }); clickCountButton(renderer); clickToggledButton(renderer); @@ -81,7 +85,7 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'preserves fixture state change (via setFixtureState) on default value change', { rendererId, fixtures }, async ({ @@ -92,10 +96,10 @@ testFixtureLoader( getLastFixtureState, fixtureStateChange, }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, { countText: '0', toggledText: 'false' }); const fixtureState = await getLastFixtureState(); - await setFixtureState({ + setFixtureState({ rendererId, fixtureId, fixtureState: { @@ -138,11 +142,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'cleans up fixture state on input rename', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, { countText: '0', toggledText: 'false' }); clickCountButton(renderer); await fixtureStateChange({ @@ -234,5 +238,7 @@ function getButtonText(rendererNode: ReactTestRendererJSON) { } function toggleButton(rendererNode: ReactTestRendererJSON) { - rendererNode.props.onClick(); + act(() => { + rendererNode.props.onClick(); + }); } diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesNumber.tsx b/packages/react-cosmos-core/src/renderer/__tests__/valuesNumber.tsx similarity index 76% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesNumber.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/valuesNumber.tsx index 9545b3311f..0059c93ca0 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesNumber.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/valuesNumber.tsx @@ -1,11 +1,15 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useValue } from '../../../fixture/useValue/index.js'; -import { createValue } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { + act, + ReactTestRenderer, + ReactTestRendererJSON, +} from 'react-test-renderer'; +import { useValue } from '../../fixture/useValue/index.js'; +import { createValue } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; function createFixtures({ defaultValue }: { defaultValue: number }) { const MyComponent = () => { @@ -16,7 +20,7 @@ function createFixtures({ defaultValue }: { defaultValue: number }) { ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -25,20 +29,20 @@ const rendererId = uuid(); const fixtures = createFixtures({ defaultValue: 0 }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, '0 clicks'); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -56,11 +60,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates fixture state via setter', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, '0 clicks'); clickButton(renderer); clickButton(renderer); @@ -81,11 +85,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'resets fixture state on default value change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, '0 clicks'); update({ rendererId, @@ -117,7 +121,9 @@ async function rendered(renderer: ReactTestRenderer, text: string) { } function clickButton(renderer: ReactTestRenderer) { - getSingleRendererElement(renderer).props.onClick(); + act(() => { + getSingleRendererElement(renderer).props.onClick(); + }); } function getSingleRendererElement(renderer: ReactTestRenderer) { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesObject.tsx b/packages/react-cosmos-core/src/renderer/__tests__/valuesObject.tsx similarity index 84% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesObject.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/valuesObject.tsx index c205c82432..32e652049f 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesObject.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/valuesObject.tsx @@ -1,11 +1,15 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useValue } from '../../../fixture/useValue/index.js'; -import { createValue } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { + act, + ReactTestRenderer, + ReactTestRendererJSON, +} from 'react-test-renderer'; +import { useValue } from '../../fixture/useValue/index.js'; +import { createValue } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; type Profile = { isAdmin: boolean; @@ -28,7 +32,7 @@ function createFixtures({ defaultValue }: { defaultValue: Profile }) { ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -39,20 +43,20 @@ const fixtures = createFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, { isAdmin: true, name: 'Pat D', age: 45 }); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -80,11 +84,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates fixture state via setter', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, { isAdmin: true, name: 'Pat D', age: 45 }); toggleAdminButton(renderer); await fixtureStateChange({ @@ -114,11 +118,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'resets fixture state on default value change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, { isAdmin: true, name: 'Pat D', age: 45 }); update({ rendererId, @@ -171,7 +175,9 @@ async function rendered( } function toggleAdminButton(renderer: ReactTestRenderer) { - getButtonNode(renderer).props.onClick(); + act(() => { + getButtonNode(renderer).props.onClick(); + }); } function getProfileNode(renderer: ReactTestRenderer) { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesString.tsx b/packages/react-cosmos-core/src/renderer/__tests__/valuesString.tsx similarity index 73% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesString.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/valuesString.tsx index 8ed9da41e6..5b8b34326e 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/valuesString.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/valuesString.tsx @@ -1,11 +1,15 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { ReactTestRenderer, ReactTestRendererJSON } from 'react-test-renderer'; -import { useValue } from '../../../fixture/useValue/index.js'; -import { createValue } from '../../../fixtureState/createValues.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { + act, + ReactTestRenderer, + ReactTestRendererJSON, +} from 'react-test-renderer'; +import { useValue } from '../../fixture/useValue/index.js'; +import { createValue } from '../../fixtureState/createValues.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; function createFixtures({ defaultValue }: { defaultValue: string }) { const MyComponent = () => { @@ -18,7 +22,7 @@ function createFixtures({ defaultValue }: { defaultValue: string }) { /> ); }; - return wrapFixtures({ + return wrapDefaultExport({ first: , }); } @@ -27,20 +31,20 @@ const rendererId = uuid(); const fixtures = createFixtures({ defaultValue: 'Fu Barr' }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders fixture', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'Fu Barr'); } ); -testFixtureLoader( +testRenderer( 'creates fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await fixtureStateChange({ rendererId, fixtureId, @@ -58,11 +62,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates fixture state via setter', { rendererId, fixtures }, async ({ renderer, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'Fu Barr'); changeInput(renderer, 'Fu Barr Bhaz'); await fixtureStateChange({ @@ -82,11 +86,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'resets fixture state on default value change', { rendererId, fixtures }, async ({ renderer, update, selectFixture, fixtureStateChange }) => { - await selectFixture({ rendererId, fixtureId, fixtureState: {} }); + selectFixture({ rendererId, fixtureId, fixtureState: {} }); await rendered(renderer, 'Fu Barr'); update({ rendererId, @@ -109,16 +113,18 @@ testFixtureLoader( } ); -function getButtonText(renderer: ReactTestRenderer) { +function getInputValue(renderer: ReactTestRenderer) { return getSingleRendererElement(renderer).props.value; } async function rendered(renderer: ReactTestRenderer, text: string) { - await retry(() => expect(getButtonText(renderer)).toEqual(text)); + await retry(() => expect(getInputValue(renderer)).toEqual(text)); } function changeInput(renderer: ReactTestRenderer, value: string) { - getSingleRendererElement(renderer).props.onChange({ target: { value } }); + act(() => { + getSingleRendererElement(renderer).props.onChange({ target: { value } }); + }); } function getSingleRendererElement(renderer: ReactTestRenderer) { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/viewport.tsx b/packages/react-cosmos-core/src/renderer/__tests__/viewport.tsx similarity index 75% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/viewport.tsx rename to packages/react-cosmos-core/src/renderer/__tests__/viewport.tsx index e0aa990637..a09f2b3562 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/__tests__/viewport.tsx +++ b/packages/react-cosmos-core/src/renderer/__tests__/viewport.tsx @@ -1,12 +1,12 @@ import retry from '@skidding/async-retry'; import React from 'react'; -import { Viewport } from '../../../fixture/Viewport.js'; -import { uuid } from '../../../utils/uuid.js'; -import { testFixtureLoader } from '../testHelpers/index.js'; -import { wrapFixtures } from '../testHelpers/wrapFixture.js'; +import { Viewport } from '../../fixture/Viewport.js'; +import { uuid } from '../../utils/uuid.js'; +import { testRenderer } from '../testHelpers/testRenderer.js'; +import { wrapDefaultExport } from '../testHelpers/wrapDefaultExport.js'; const rendererId = uuid(); -const fixtures = wrapFixtures({ +const fixtures = wrapDefaultExport({ first: ( yo @@ -15,11 +15,11 @@ const fixtures = wrapFixtures({ }); const fixtureId = { path: 'first' }; -testFixtureLoader( +testRenderer( 'renders children', { rendererId, fixtures }, async ({ renderer, selectFixture }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, @@ -28,11 +28,11 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'creates viewport fixture state', { rendererId, fixtures }, async ({ selectFixture, fixtureStateChange }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, @@ -48,18 +48,18 @@ testFixtureLoader( } ); -testFixtureLoader( +testRenderer( 'updates viewport fixture state', { rendererId, fixtures }, async ({ update, selectFixture, fixtureStateChange }) => { - await selectFixture({ + selectFixture({ rendererId, fixtureId, fixtureState: {}, }); update({ rendererId, - fixtures: wrapFixtures({ + fixtures: wrapDefaultExport({ first: ( yo diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/postMessage.ts b/packages/react-cosmos-core/src/renderer/createPostMessageConnect.ts similarity index 86% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/postMessage.ts rename to packages/react-cosmos-core/src/renderer/createPostMessageConnect.ts index 4ea141ff8a..acfaf8ab35 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/postMessage.ts +++ b/packages/react-cosmos-core/src/renderer/createPostMessageConnect.ts @@ -1,9 +1,9 @@ -import { registerPlaygroundShortcuts } from '../registerPlaygroundShortcuts.js'; +import { registerPlaygroundShortcuts } from './registerPlaygroundShortcuts.js'; import { RendererConnect, RendererRequest, RendererResponse, -} from '../types.js'; +} from './rendererConnectTypes.js'; export function createPostMessageConnect(): RendererConnect { function postMessage(msg: RendererResponse) { diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/webSockets.ts b/packages/react-cosmos-core/src/renderer/createWebSocketsConnect.ts similarity index 90% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/webSockets.ts rename to packages/react-cosmos-core/src/renderer/createWebSocketsConnect.ts index 3f4ee124ff..adf705fa0b 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/webSockets.ts +++ b/packages/react-cosmos-core/src/renderer/createWebSocketsConnect.ts @@ -1,8 +1,8 @@ import { rendererSocketMessage, SocketMessage, -} from '../../server/socketMessage.js'; -import { RendererConnect, RendererRequest } from '../types.js'; +} from '../server/socketMessage.js'; +import { RendererConnect, RendererRequest } from './rendererConnectTypes.js'; export function createWebSocketsConnect(url: string): RendererConnect { let pendingMessages: SocketMessage[] = []; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/getDecoratedFixtureElement/FixtureElement.tsx b/packages/react-cosmos-core/src/renderer/getDecoratedFixtureElement/FixtureElement.tsx similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/getDecoratedFixtureElement/FixtureElement.tsx rename to packages/react-cosmos-core/src/renderer/getDecoratedFixtureElement/FixtureElement.tsx diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/getDecoratedFixtureElement/index.tsx b/packages/react-cosmos-core/src/renderer/getDecoratedFixtureElement/index.tsx similarity index 96% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/getDecoratedFixtureElement/index.tsx rename to packages/react-cosmos-core/src/renderer/getDecoratedFixtureElement/index.tsx index eaf908076e..b7695a4e27 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/getDecoratedFixtureElement/index.tsx +++ b/packages/react-cosmos-core/src/renderer/getDecoratedFixtureElement/index.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import { FixtureCapture } from '../FixtureCapture/index.js'; import { ReactDecorator, ReactDecoratorProps, ReactFixture, -} from '../../reactTypes.js'; -import { FixtureCapture } from '../FixtureCapture/index.js'; +} from '../userModuleTypes.js'; import { FixtureElement } from './FixtureElement.js'; export function getDecoratedFixtureElement( diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/fixtureHelpers.ts b/packages/react-cosmos-core/src/renderer/getFixture.ts similarity index 64% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/fixtureHelpers.ts rename to packages/react-cosmos-core/src/renderer/getFixture.ts index 5229a92ec5..aa423e18c5 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/fixtureHelpers.ts +++ b/packages/react-cosmos-core/src/renderer/getFixture.ts @@ -1,5 +1,5 @@ -import { isMultiFixture } from '../isMultiFixture.js'; -import { ReactFixture, ReactFixtureExport } from '../reactTypes.js'; +import { isMultiFixture } from './isMultiFixture.js'; +import { ReactFixture, ReactFixtureExport } from './userModuleTypes.js'; export function getFixture( fixtureExport: ReactFixtureExport, @@ -8,7 +8,8 @@ export function getFixture( if (fixtureName === undefined) { if (isMultiFixture(fixtureExport)) { // Fixture name missing in multi fixture - return; + const fixtureNames = Object.keys(fixtureExport); + return fixtureExport[fixtureNames[0]]; } return fixtureExport; diff --git a/packages/react-cosmos-core/src/renderer/getFixtureList.ts b/packages/react-cosmos-core/src/renderer/getFixtureList.ts index cfff2f27e7..0619a7e2ef 100644 --- a/packages/react-cosmos-core/src/renderer/getFixtureList.ts +++ b/packages/react-cosmos-core/src/renderer/getFixtureList.ts @@ -1,29 +1,39 @@ import { FixtureList, FixtureListItem } from '../fixture/types.js'; import { isMultiFixture } from './isMultiFixture.js'; import { + ByPath, ReactFixtureExport, - ReactFixtureExports, - ReactFixtureWrapper, - ReactFixtureWrappers, -} from './reactTypes.js'; + UserModuleWrappers, +} from './userModuleTypes.js'; -export function getFixtureListFromWrappers(wrappers: ReactFixtureWrappers) { - return Object.keys(wrappers).reduce((acc: FixtureList, fixturePath) => { - return { ...acc, [fixturePath]: getItemFromWrapper(wrappers[fixturePath]) }; - }, {}); +export function getFixtureListFromWrappers(wrappers: UserModuleWrappers) { + return Object.keys(wrappers.fixtures).reduce( + (acc, fixturePath) => { + return { + ...acc, + [fixturePath]: wrappers.lazy + ? { type: 'single' } + : getFixtureItemFromExport( + wrappers.fixtures[fixturePath].module.default + ), + }; + }, + {} + ); } -export function getFixtureListFromExports(exports: ReactFixtureExports) { +export function getFixtureListFromExports(exports: ByPath) { return Object.keys(exports).reduce((acc: FixtureList, fixturePath) => { - return { ...acc, [fixturePath]: getItemFromExport(exports[fixturePath]) }; + return { + ...acc, + [fixturePath]: getFixtureItemFromExport(exports[fixturePath]), + }; }, {}); } -function getItemFromWrapper(wrapper: ReactFixtureWrapper): FixtureListItem { - return getItemFromExport(wrapper.module.default); -} - -function getItemFromExport(fixtureExport: ReactFixtureExport): FixtureListItem { +export function getFixtureItemFromExport( + fixtureExport: ReactFixtureExport +): FixtureListItem { return isMultiFixture(fixtureExport) ? { type: 'multi', fixtureNames: Object.keys(fixtureExport) } : { type: 'single' }; diff --git a/packages/react-cosmos-core/src/renderer/getSortedDecoratorsForFixturePath.ts b/packages/react-cosmos-core/src/renderer/getSortedDecoratorsForFixturePath.ts index 2a9d033ee9..a0d3279d2d 100644 --- a/packages/react-cosmos-core/src/renderer/getSortedDecoratorsForFixturePath.ts +++ b/packages/react-cosmos-core/src/renderer/getSortedDecoratorsForFixturePath.ts @@ -1,16 +1,16 @@ -import { ReactDecorator, ReactDecorators } from './reactTypes.js'; +import { ByPath } from './userModuleTypes.js'; -export function getSortedDecoratorsForFixturePath( +export function getSortedDecoratorsForFixturePath( fixturePath: string, - decoratorsByPath: ReactDecorators -): ReactDecorator[] { + decoratorsByPath: ByPath +): T[] { return getSortedDecorators( getDecoratorsForFixturePath(decoratorsByPath, fixturePath) ); } -function getDecoratorsForFixturePath( - decoratorsByPath: ReactDecorators, +function getDecoratorsForFixturePath( + decoratorsByPath: ByPath, fixturePath: string ) { return Object.keys(decoratorsByPath) @@ -28,9 +28,7 @@ function getParentPath(nestedPath: string) { return nestedPath.replace(/^((.+)\/)?.+$/, '$2'); } -function getSortedDecorators( - decoratorsByPath: ReactDecorators -): ReactDecorator[] { +function getSortedDecorators(decoratorsByPath: ByPath): T[] { return sortPathsByDepthAsc(Object.keys(decoratorsByPath)).map( decoratorPath => decoratorsByPath[decoratorPath] ); diff --git a/packages/react-cosmos-core/src/renderer/isMultiFixture.ts b/packages/react-cosmos-core/src/renderer/isMultiFixture.ts index 8a80241ac0..037a9337ad 100644 --- a/packages/react-cosmos-core/src/renderer/isMultiFixture.ts +++ b/packages/react-cosmos-core/src/renderer/isMultiFixture.ts @@ -1,5 +1,5 @@ import { isElement } from 'react-is'; -import { ReactFixtureExport, ReactFixtureMap } from './reactTypes.js'; +import { ReactFixtureExport, ReactFixtureMap } from './userModuleTypes.js'; export function isMultiFixture( fixtureExport: ReactFixtureExport diff --git a/packages/react-cosmos-core/src/renderer/reactTypes.ts b/packages/react-cosmos-core/src/renderer/reactTypes.ts deleted file mode 100644 index 72bbfc3b75..0000000000 --- a/packages/react-cosmos-core/src/renderer/reactTypes.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ComponentType, FunctionComponent, ReactNode } from 'react'; -import { FixtureState, SetFixtureState } from '../fixtureState/types.js'; - -// These generic types keep Cosmos slightly more decoupled from React -type FixtureMap = { [fixtureName: string]: FixtureType }; -type FixtureExport = FixtureType | FixtureMap; -type FixtureModule = { default: FixtureExport }; -type FixtureWrapper = { module: FixtureModule }; - -export type ReactFixture = ReactNode | FunctionComponent; -export type ReactFixtureMap = FixtureMap; -export type ReactFixtureExport = FixtureExport; -export type ReactFixtureModule = FixtureModule; -export type ReactFixtureWrapper = FixtureWrapper; - -export type ReactDecoratorProps = { - children: ReactNode; - fixtureState: FixtureState; - setFixtureState: SetFixtureState; - onErrorReset: () => unknown; -}; -export type ReactDecorator = ComponentType; - -export type ReactFixtureExports = Record; -export type ReactFixtureWrappers = Record; -export type ReactDecorators = Record; diff --git a/packages/react-cosmos-core/src/renderer/types.ts b/packages/react-cosmos-core/src/renderer/rendererConnectTypes.ts similarity index 89% rename from packages/react-cosmos-core/src/renderer/types.ts rename to packages/react-cosmos-core/src/renderer/rendererConnectTypes.ts index 9faab039c9..2d69c93152 100644 --- a/packages/react-cosmos-core/src/renderer/types.ts +++ b/packages/react-cosmos-core/src/renderer/rendererConnectTypes.ts @@ -1,4 +1,4 @@ -import { FixtureId, FixtureList } from '../fixture/types.js'; +import { FixtureId, FixtureList, FixtureListItem } from '../fixture/types.js'; import { FixtureState } from '../fixtureState/types.js'; // FYI: Renderer ids are self assigned in remote environments, so uniqueness @@ -66,6 +66,15 @@ export type FixtureListUpdateResponse = { }; }; +export type FixtureListItemUpdateResponse = { + type: 'fixtureListItemUpdate'; + payload: { + rendererId: RendererId; + fixturePath: string; + fixtureItem: FixtureListItem; + }; +}; + // Caused by an organic state change inside the renderer. Also dispatched // after a fixtureSelect request, when rendering stateful components, as their // initial state is read. @@ -92,6 +101,7 @@ export type RendererResponse = | RendererReadyResponse | RendererErrorResponse | FixtureListUpdateResponse + | FixtureListItemUpdateResponse | FixtureStateChangeResponse | PlaygroundCommandResponse; diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/components.tsx b/packages/react-cosmos-core/src/renderer/testHelpers/components.tsx similarity index 100% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/components.tsx rename to packages/react-cosmos-core/src/renderer/testHelpers/components.tsx diff --git a/packages/react-cosmos-core/src/renderer/testHelpers/createRendererConnectTestApi.ts b/packages/react-cosmos-core/src/renderer/testHelpers/createRendererConnectTestApi.ts new file mode 100644 index 0000000000..8142ceb67e --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/testHelpers/createRendererConnectTestApi.ts @@ -0,0 +1,169 @@ +import until from 'async-until'; +import { findLast } from 'lodash-es'; +import { FixtureState } from '../../fixtureState/types.js'; +import { + FixtureListItemUpdateResponse, + FixtureListUpdateResponse, + FixtureStateChangeResponse, + RendererReadyResponse, + RendererRequest, + RendererResponse, + SelectFixtureRequest, + SetFixtureStateRequest, + UnselectFixtureRequest, +} from '../rendererConnectTypes.js'; + +export type RendererConnectTestApi = { + pingRenderers: () => void; + selectFixture: (payload: SelectFixtureRequest['payload']) => void; + unselectFixture: (payload: UnselectFixtureRequest['payload']) => void; + setFixtureState: (payload: SetFixtureStateRequest['payload']) => void; + rendererReady: (payload: RendererReadyResponse['payload']) => Promise; + fixtureListUpdate: ( + payload: FixtureListUpdateResponse['payload'] + ) => Promise; + fixtureListItemUpdate: ( + payload: FixtureListItemUpdateResponse['payload'] + ) => Promise; + fixtureStateChange: ( + payload: FixtureStateChangeResponse['payload'] + ) => Promise; + getLastFixtureState: () => Promise; +}; + +export function createRendererConnectTestApi(args: { + getResponses: () => RendererResponse[]; + postRequest: (msg: RendererRequest) => unknown | Promise; +}): RendererConnectTestApi { + return { + pingRenderers, + selectFixture, + unselectFixture, + setFixtureState, + rendererReady, + fixtureListUpdate, + fixtureListItemUpdate, + fixtureStateChange, + getLastFixtureState, + }; + + function pingRenderers() { + return args.postRequest({ + type: 'pingRenderers', + }); + } + + function selectFixture(payload: SelectFixtureRequest['payload']) { + return args.postRequest({ + type: 'selectFixture', + payload, + }); + } + + function unselectFixture(payload: UnselectFixtureRequest['payload']) { + return args.postRequest({ + type: 'unselectFixture', + payload, + }); + } + + function setFixtureState(payload: SetFixtureStateRequest['payload']) { + return args.postRequest({ + type: 'setFixtureState', + payload, + }); + } + + async function rendererReady(payload: RendererReadyResponse['payload']) { + await untilResponse({ + type: 'rendererReady', + payload, + }); + } + + async function fixtureListUpdate( + payload: FixtureListUpdateResponse['payload'] + ) { + await untilResponse({ + type: 'fixtureListUpdate', + payload, + }); + } + + async function fixtureListItemUpdate( + payload: FixtureListItemUpdateResponse['payload'] + ) { + await untilResponse({ + type: 'fixtureListItemUpdate', + payload, + }); + } + + async function fixtureStateChange( + payload: FixtureStateChangeResponse['payload'] + ) { + await untilResponse({ + type: 'fixtureStateChange', + payload, + }); + } + + async function getLastFixtureState() { + const msg = await getLastResponseOfType( + 'fixtureStateChange' + ); + return msg.payload.fixtureState; + } + + async function untilResponse(msg: RendererResponse) { + try { + await until( + () => { + try { + // Support expect.any(constructor) matches + // https://jestjs.io/docs/en/expect#expectanyconstructor + expect(findLastResponseWithType(msg.type)).toEqual(msg); + return true; + } catch (err) { + return false; + } + }, + { timeout: 1000 } + ); + } catch (err) { + expect(findLastResponseWithType(msg.type)).toEqual(msg); + } + } + + async function getLastResponseOfType( + msgType: string + ): Promise { + let lastMsg = null as null | RendererResponse; + + try { + await until( + () => { + lastMsg = getLastResponse(); + return lastMsg && lastMsg.type === msgType; + }, + { timeout: 1000 } + ); + } finally { + if (!lastMsg || lastMsg.type !== msgType) { + throw new Error(`"${msgType}" message never arrived`); + } + } + + return lastMsg as M; + } + + function getLastResponse(): null | RendererResponse { + const messages = args.getResponses(); + return messages.length === 0 ? null : messages[messages.length - 1]; + } + + function findLastResponseWithType(type: string): null | RendererResponse { + const messages = args.getResponses(); + return findLast(messages, msg => msg.type === type) ?? null; + } +} diff --git a/packages/react-cosmos-core/src/renderer/testHelpers/createTestRendererConnect.ts b/packages/react-cosmos-core/src/renderer/testHelpers/createTestRendererConnect.ts new file mode 100644 index 0000000000..04420c8473 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/testHelpers/createTestRendererConnect.ts @@ -0,0 +1,41 @@ +import delay from 'delay'; +import { act } from 'react-test-renderer'; +import { + RendererConnect, + RendererRequest, + RendererResponse, +} from '../rendererConnectTypes.js'; + +type Args = { + onRendererResponse: (msg: RendererResponse) => unknown; +}; +export function createTestRendererConnect({ onRendererResponse }: Args) { + let messageHandlers: ((msg: RendererRequest) => unknown)[] = []; + + const rendererConnect: RendererConnect = { + postMessage(rendererResponse) { + onRendererResponse(rendererResponse); + }, + + onMessage(onMessage) { + messageHandlers = [...messageHandlers, onMessage]; + return () => { + messageHandlers = messageHandlers.filter( + handler => handler !== onMessage + ); + }; + }, + }; + + async function postRendererRequest(rendererRequest: RendererRequest) { + // Simulate async communication between renderer and parent + await delay(Math.round(Math.random() * 50)); + act(() => { + messageHandlers.forEach(handler => { + handler(rendererRequest); + }); + }); + } + + return { rendererConnect, postRendererRequest }; +} diff --git a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/fixtureState.ts b/packages/react-cosmos-core/src/renderer/testHelpers/fixtureState.ts similarity index 97% rename from packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/fixtureState.ts rename to packages/react-cosmos-core/src/renderer/testHelpers/fixtureState.ts index d27c86874c..a172828b64 100644 --- a/packages/react-cosmos-core/src/renderer/FixtureLoader/testHelpers/fixtureState.ts +++ b/packages/react-cosmos-core/src/renderer/testHelpers/fixtureState.ts @@ -4,7 +4,7 @@ import { FixtureStateClassState, FixtureStateProps, FixtureStateValues, -} from '../../../fixtureState/types.js'; +} from '../../fixtureState/types.js'; export function anyProps( args: { diff --git a/packages/react-cosmos-core/src/renderer/testHelpers/mountTestRenderer.tsx b/packages/react-cosmos-core/src/renderer/testHelpers/mountTestRenderer.tsx new file mode 100644 index 0000000000..7657e61d42 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/testHelpers/mountTestRenderer.tsx @@ -0,0 +1,123 @@ +import delay from 'delay'; +import { mapValues } from 'lodash-es'; +import React from 'react'; +import { act, create, ReactTestRenderer } from 'react-test-renderer'; +import { FixtureId } from '../../fixture/types.js'; +import { FixtureConnect } from '../FixtureConnect.js'; +import { + RendererConnect, + RendererId, + RendererResponse, +} from '../rendererConnectTypes.js'; +import { + ByPath, + ReactDecoratorModule, + ReactFixtureModule, + UserModuleWrappers, +} from '../userModuleTypes.js'; +import { + createRendererConnectTestApi, + RendererConnectTestApi, +} from './createRendererConnectTestApi.js'; +import { createTestRendererConnect } from './createTestRendererConnect.js'; + +export type RendererTestArgs = { + rendererId: RendererId; + fixtures: ByPath; + selectedFixtureId?: null | FixtureId; + initialFixtureId?: FixtureId; + decorators?: ByPath; + lazy?: boolean; + only?: boolean; + onErrorReset?: () => unknown; +}; + +type RendererTestApi = RendererConnectTestApi & { + renderer: ReactTestRenderer; + update: (args: RendererTestArgs) => Promise; +}; + +export type RendererTestCallback = (api: RendererTestApi) => Promise; + +export async function mountTestRenderer( + args: RendererTestArgs, + cb: RendererTestCallback +) { + expect.hasAssertions(); + + let responses: RendererResponse[] = []; + const { rendererConnect, postRendererRequest } = createTestRendererConnect({ + onRendererResponse(response) { + responses.push(response); + }, + }); + + // This act() call ensures the component is subscribed to renderer requests + // before the test is executed. + let renderer = undefined as any as ReactTestRenderer; + act(() => { + renderer = create(getElement(rendererConnect, args)); + }); + try { + await cb({ + renderer, + update: async newArgs => { + act(() => { + renderer.update(getElement(rendererConnect, newArgs)); + }); + }, + ...createRendererConnectTestApi({ + getResponses: () => responses, + postRequest: postRendererRequest, + }), + }); + } finally { + renderer.unmount(); + } +} + +function getElement(rendererConnect: RendererConnect, args: RendererTestArgs) { + const { fixtures, decorators = {}, lazy = false, ...otherArgs } = args; + return ( + + ); +} + +function getModuleWrappers( + fixtures: ByPath, + decorators: ByPath, + lazy: boolean +): UserModuleWrappers { + if (lazy) { + return { + lazy: true, + fixtures: mapValues(fixtures, fixture => ({ + getModule: () => dynamicImportWrapper(fixture), + })), + decorators: mapValues(decorators, decorator => ({ + getModule: () => dynamicImportWrapper(decorator), + })), + }; + } else { + return { + lazy: false, + fixtures: mapValues(fixtures, fixture => ({ module: fixture })), + decorators: mapValues(decorators, decorator => ({ module: decorator })), + }; + } +} + +function dynamicImportWrapper(module: T) { + return new Promise(async resolve => { + // Simulate module download time + await delay(Math.round(Math.random() * 50)); + await act(() => { + resolve(module); + }); + }); +} diff --git a/packages/react-cosmos-core/src/renderer/testHelpers/testRenderer.ts b/packages/react-cosmos-core/src/renderer/testHelpers/testRenderer.ts new file mode 100644 index 0000000000..03acf414bf --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/testHelpers/testRenderer.ts @@ -0,0 +1,19 @@ +import { + mountTestRenderer, + RendererTestArgs, + RendererTestCallback, +} from './mountTestRenderer.js'; + +export function testRenderer( + testName: string, + args: RendererTestArgs, + cb: RendererTestCallback +) { + const test = () => mountTestRenderer(args, cb); + + if (args.only) { + it.only(testName, test); + } else { + it(testName, test); + } +} diff --git a/packages/react-cosmos-core/src/renderer/testHelpers/wrapActSetTimeout.ts b/packages/react-cosmos-core/src/renderer/testHelpers/wrapActSetTimeout.ts new file mode 100644 index 0000000000..36379069e8 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/testHelpers/wrapActSetTimeout.ts @@ -0,0 +1,14 @@ +import { act } from 'react-test-renderer'; + +export function wrapActSetTimeout() { + const { setTimeout } = window; + + // @ts-ignore + window.setTimeout = (cb: () => void, timeout: number) => { + return setTimeout(() => { + act(() => { + cb(); + }); + }, timeout); + }; +} diff --git a/packages/react-cosmos-core/src/renderer/testHelpers/wrapDefaultExport.ts b/packages/react-cosmos-core/src/renderer/testHelpers/wrapDefaultExport.ts new file mode 100644 index 0000000000..7fadc09c07 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/testHelpers/wrapDefaultExport.ts @@ -0,0 +1,6 @@ +import { mapValues } from 'lodash-es'; +import { ByPath } from '../userModuleTypes.js'; + +export function wrapDefaultExport(modules: ByPath) { + return mapValues(modules, defaultExport => ({ default: defaultExport })); +} diff --git a/packages/react-cosmos-core/src/renderer/useFixtureModules.ts b/packages/react-cosmos-core/src/renderer/useFixtureModules.ts new file mode 100644 index 0000000000..b816bcf3a9 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/useFixtureModules.ts @@ -0,0 +1,20 @@ +import { useMemo } from 'react'; +import { + FixtureModules, + ReactDecoratorWrapper, + ReactFixtureWrapper, +} from './userModuleTypes.js'; + +export function useFixtureModules( + fixturePath: string, + fixtureWrapper: ReactFixtureWrapper, + decoratorWrappers: ReactDecoratorWrapper[] +) { + return useMemo(() => { + return { + fixturePath, + fixtureModule: fixtureWrapper.module, + decoratorModules: decoratorWrappers.map(d => d.module), + }; + }, [decoratorWrappers, fixturePath, fixtureWrapper]); +} diff --git a/packages/react-cosmos-core/src/renderer/useLazyFixtureModules.ts b/packages/react-cosmos-core/src/renderer/useLazyFixtureModules.ts new file mode 100644 index 0000000000..113cc7e73b --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/useLazyFixtureModules.ts @@ -0,0 +1,42 @@ +import { useEffect, useState } from 'react'; +import { + FixtureModules, + LazyReactDecoratorWrapper, + LazyReactFixtureWrapper, +} from './userModuleTypes.js'; + +export function useLazyFixtureModules( + fixturePath: string, + fixtureWrapper: LazyReactFixtureWrapper, + decoratorWrappers: LazyReactDecoratorWrapper[] +) { + const [modules, setModules] = useState(null); + + useEffect(() => { + let canceled = false; + + (async () => { + const fixtureModule = await fixtureWrapper.getModule(); + + const decoratorModules = await Promise.all( + decoratorWrappers.map(d => d.getModule()) + ); + + if (!canceled) { + setModules({ + fixturePath, + fixtureModule, + decoratorModules, + }); + } + })(); + + return () => { + canceled = true; + }; + }, [decoratorWrappers, fixturePath, fixtureWrapper]); + + // Stop returning modules once fixturePath changed to prevent rendering + // the previous fixture until the new fixture modules are loaded + return modules && modules.fixturePath === fixturePath ? modules : null; +} diff --git a/packages/react-cosmos-core/src/renderer/useRendererRequest.ts b/packages/react-cosmos-core/src/renderer/useRendererRequest.ts new file mode 100644 index 0000000000..bcdcfa21e7 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/useRendererRequest.ts @@ -0,0 +1,105 @@ +import { isEqual } from 'lodash-es'; +import { Dispatch, SetStateAction, useEffect } from 'react'; +import { getFixtureListFromWrappers } from './getFixtureList.js'; +import { + RendererConnect, + RendererRequest, + SelectFixtureRequest, + SetFixtureStateRequest, +} from './rendererConnectTypes.js'; +import { UserModuleWrappers } from './userModuleTypes.js'; +import { SelectedFixture } from './useSelectedFixture.js'; + +export function useRendererRequest( + rendererId: string, + rendererConnect: RendererConnect, + moduleWrappers: UserModuleWrappers, + setSelectedFixture: Dispatch>, + onErrorReset?: () => unknown +) { + useEffect(() => { + function postReadyState() { + const fixtures = getFixtureListFromWrappers(moduleWrappers); + rendererConnect.postMessage({ + type: 'rendererReady', + payload: { rendererId, fixtures }, + }); + } + + function fireChangeCallback() { + if (typeof onErrorReset === 'function') { + onErrorReset(); + } + } + + function handleSelectFixtureRequest({ payload }: SelectFixtureRequest) { + const { fixtureId, fixtureState } = payload; + setSelectedFixture(prev => { + return { + fixtureId, + fixtureState, + syncedFixtureState: fixtureState, + renderKey: prev ? prev.renderKey + 1 : 0, + }; + }); + } + + function handleUnselectFixtureRequest() { + setSelectedFixture(null); + } + + function handleSetFixtureStateRequest({ payload }: SetFixtureStateRequest) { + const { fixtureId, fixtureState } = payload; + setSelectedFixture(prev => { + // Ensure fixture state applies to currently selected fixture + if (prev && isEqual(prev.fixtureId, fixtureId)) { + return { + ...prev, + fixtureState, + syncedFixtureState: fixtureState, + }; + } else { + return prev; + } + }); + } + + const unsubscribe = rendererConnect.onMessage((msg: RendererRequest) => { + if (msg.type === 'pingRenderers') { + return postReadyState(); + } + + if (!msg.payload || msg.payload.rendererId !== rendererId) { + return; + } + + const doesRequestChangeFixture = + msg.type === 'selectFixture' || msg.type === 'unselectFixture'; + if (doesRequestChangeFixture) { + fireChangeCallback(); + } + + switch (msg.type) { + case 'selectFixture': + return handleSelectFixtureRequest(msg); + case 'unselectFixture': + return handleUnselectFixtureRequest(); + case 'setFixtureState': + return handleSetFixtureStateRequest(msg); + default: + // Ignore all other messages, which could be unrelated browser + // devtools communications. + } + }); + + return () => { + unsubscribe(); + }; + }, [ + moduleWrappers, + onErrorReset, + rendererConnect, + rendererId, + setSelectedFixture, + ]); +} diff --git a/packages/react-cosmos-core/src/renderer/useRendererResponse.ts b/packages/react-cosmos-core/src/renderer/useRendererResponse.ts new file mode 100644 index 0000000000..d95ddfb6bd --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/useRendererResponse.ts @@ -0,0 +1,33 @@ +import { useEffect, useRef } from 'react'; +import { FixtureId } from '../fixture/types.js'; +import { getFixtureListFromWrappers } from './getFixtureList.js'; +import { RendererConnect } from './rendererConnectTypes.js'; +import { UserModuleWrappers } from './userModuleTypes.js'; + +export function useRendererResponse( + rendererId: string, + rendererConnect: RendererConnect, + moduleWrappers: UserModuleWrappers, + initialFixtureId?: FixtureId +) { + const readyRef = useRef(false); + + useEffect(() => { + const fixtures = getFixtureListFromWrappers(moduleWrappers); + + if (readyRef.current) { + rendererConnect.postMessage({ + type: 'fixtureListUpdate', + payload: { rendererId, fixtures }, + }); + } else { + rendererConnect.postMessage({ + type: 'rendererReady', + payload: initialFixtureId + ? { rendererId, fixtures, initialFixtureId } + : { rendererId, fixtures }, + }); + readyRef.current = true; + } + }, [initialFixtureId, moduleWrappers, rendererConnect, rendererId]); +} diff --git a/packages/react-cosmos-core/src/renderer/useSelectedFixture.ts b/packages/react-cosmos-core/src/renderer/useSelectedFixture.ts new file mode 100644 index 0000000000..5e49df9f2e --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/useSelectedFixture.ts @@ -0,0 +1,51 @@ +import { useCallback, useState } from 'react'; +import { FixtureId } from '../fixture/types.js'; +import { FixtureState, SetFixtureState } from '../fixtureState/types.js'; + +export type SelectedFixture = { + fixtureId: FixtureId; + fixtureState: FixtureState; + syncedFixtureState: FixtureState; + renderKey: number; +}; + +export function useSelectedFixture( + initialFixtureId?: FixtureId, + selectedFixtureId?: null | FixtureId +) { + const [selectedFixture, setSelectedFixture] = + useState(() => { + const fixtureId = selectedFixtureId ?? initialFixtureId; + if (!fixtureId) return null; + + return { + fixtureId, + fixtureState: {}, + syncedFixtureState: {}, + renderKey: 0, + }; + }); + + const setFixtureState = useCallback( + stateUpdate => { + setSelectedFixture(prevState => { + if (!prevState) { + console.warn('Trying to set fixture state with no fixture selected'); + return null; + } + + return { + ...prevState, + fixtureState: stateUpdate(prevState.fixtureState), + }; + }); + }, + [setSelectedFixture] + ); + + return { + selectedFixture, + setSelectedFixture, + setFixtureState, + }; +} diff --git a/packages/react-cosmos-core/src/renderer/userModuleTypes.ts b/packages/react-cosmos-core/src/renderer/userModuleTypes.ts new file mode 100644 index 0000000000..e3d663c4f4 --- /dev/null +++ b/packages/react-cosmos-core/src/renderer/userModuleTypes.ts @@ -0,0 +1,49 @@ +import { ComponentType, FunctionComponent, ReactNode } from 'react'; +import { FixtureState, SetFixtureState } from '../fixtureState/types.js'; + +// These generic types keep Cosmos slightly more decoupled from React + +type FixtureMap = { [fixtureName: string]: FixtureType }; +type FixtureExport = FixtureType | FixtureMap; +type FixtureModule = { default: FixtureExport }; + +type ModuleWrapper = { module: ModuleType }; +type LazyModuleWrapper = { getModule: () => Promise }; + +export type ReactFixture = ReactNode | FunctionComponent; +export type ReactFixtureMap = FixtureMap; +export type ReactFixtureExport = FixtureExport; +export type ReactFixtureModule = FixtureModule; +export type ReactFixtureWrapper = ModuleWrapper; +export type LazyReactFixtureWrapper = LazyModuleWrapper; + +export type ReactDecoratorProps = { + children: ReactNode; + fixtureState: FixtureState; + setFixtureState: SetFixtureState; + onErrorReset: () => unknown; +}; +export type ReactDecorator = ComponentType; +export type ReactDecoratorModule = { default: ReactDecorator }; +export type ReactDecoratorWrapper = ModuleWrapper; +export type LazyReactDecoratorWrapper = LazyModuleWrapper; + +export type ByPath = Record; + +export type UserModuleWrappers = + | { + lazy: true; + fixtures: ByPath; + decorators: ByPath; + } + | { + lazy: false; + fixtures: ByPath; + decorators: ByPath; + }; + +export type FixtureModules = { + fixturePath: string; + fixtureModule: ReactFixtureModule; + decoratorModules: ReactDecoratorModule[]; +}; diff --git a/packages/react-cosmos-dom/src/DomFixtureLoader.tsx b/packages/react-cosmos-dom/src/DomFixtureLoader.tsx index 52f2030a25..660b3fcdf3 100644 --- a/packages/react-cosmos-dom/src/DomFixtureLoader.tsx +++ b/packages/react-cosmos-dom/src/DomFixtureLoader.tsx @@ -1,26 +1,18 @@ import React, { useEffect, useMemo } from 'react'; -import { - FixtureLoader, - ReactDecorators, - ReactFixtureWrappers, -} from 'react-cosmos-core'; +import { FixtureConnect, UserModuleWrappers } from 'react-cosmos-core'; import { createDomRendererConnect } from './domRendererConnect.js'; import { domRendererId } from './domRendererId.js'; import { ErrorCatch } from './ErrorCatch.js'; import { selectedFixtureId } from './selectedFixtureId.js'; type Props = { - fixtures: ReactFixtureWrappers; - decorators: ReactDecorators; + moduleWrappers: UserModuleWrappers; playgroundUrl: string; onErrorReset?: () => unknown; }; -export function DomFixtureLoader({ - fixtures, - decorators, - playgroundUrl, - onErrorReset, -}: Props) { +export function DomFixtureLoader(props: Props) { + const { moduleWrappers, playgroundUrl, onErrorReset } = props; + const domRendererConnect = useMemo( () => createDomRendererConnect(playgroundUrl), [playgroundUrl] @@ -45,19 +37,20 @@ export function DomFixtureLoader({ }, [domRendererConnect]); return ( - ); } +const systemDecorators = [ErrorCatch]; + const containerStyle: React.CSSProperties = { position: 'absolute', top: 0, @@ -72,6 +65,6 @@ const containerStyle: React.CSSProperties = { fontSize: 14, }; -export function renderDomMessage({ msg }: { msg: string }) { +export function renderDomMessage(msg: string) { return
{msg}
; } diff --git a/packages/react-cosmos-dom/src/mountDomRenderer.tsx b/packages/react-cosmos-dom/src/mountDomRenderer.tsx index d3a249cba6..ea8bd877ad 100644 --- a/packages/react-cosmos-dom/src/mountDomRenderer.tsx +++ b/packages/react-cosmos-dom/src/mountDomRenderer.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ReactDecorators, ReactFixtureWrappers } from 'react-cosmos-core'; +import { UserModuleWrappers } from 'react-cosmos-core'; import { createRoot } from 'react-dom/client'; import { DomFixtureLoader } from './DomFixtureLoader.js'; import { getDomContainer } from './getDomContainer.js'; @@ -13,14 +13,12 @@ let cachedRoot: CachedRoot | null = null; type Args = { rendererConfig: DomRendererConfig; - fixtures: ReactFixtureWrappers; - decorators: ReactDecorators; + moduleWrappers: UserModuleWrappers; onErrorReset?: () => unknown; }; export function mountDomRenderer({ rendererConfig, - fixtures, - decorators, + moduleWrappers, onErrorReset, }: Args) { const domContainer = getDomContainer(rendererConfig.containerQuerySelector); @@ -31,8 +29,7 @@ export function mountDomRenderer({ cachedRoot.reactRoot.render( diff --git a/packages/react-cosmos-native/src/NativeFixtureLoader.tsx b/packages/react-cosmos-native/src/NativeFixtureLoader.tsx index 3ae902866c..322e6c5849 100644 --- a/packages/react-cosmos-native/src/NativeFixtureLoader.tsx +++ b/packages/react-cosmos-native/src/NativeFixtureLoader.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { createWebSocketsConnect, + FixtureConnect, FixtureId, - FixtureLoader, - ReactDecorators, - ReactFixtureWrappers, RendererConfig, + UserModuleWrappers, } from 'react-cosmos-core'; import * as ReactNative from 'react-native'; import { getSocketUrl } from './getSocketUrl.js'; @@ -17,32 +16,29 @@ const rendererId = 'native-renderer'; type Props = { rendererConfig: RendererConfig; - fixtures: ReactFixtureWrappers; - decorators: ReactDecorators; + moduleWrappers: UserModuleWrappers; initialFixtureId?: FixtureId; }; export function NativeFixtureLoader({ rendererConfig: { playgroundUrl }, - fixtures, - decorators, + moduleWrappers, initialFixtureId, }: Props) { const socketUrl = getSocketUrl(playgroundUrl); return ( - ); } -function renderMessage({ msg }: { msg: string }) { +function renderMessage(msg: string) { return ( {msg} diff --git a/packages/react-cosmos-plugin-vite/package.json b/packages/react-cosmos-plugin-vite/package.json index 5f6cfe6b89..2d3acb619a 100644 --- a/packages/react-cosmos-plugin-vite/package.json +++ b/packages/react-cosmos-plugin-vite/package.json @@ -7,6 +7,7 @@ "type": "module", "main": "cosmos.plugin.json", "dependencies": { + "@vitejs/plugin-react": "^3.1.0", "react-cosmos-core": "^6.0.0-alpha.11", "react-cosmos-dom": "^6.0.0-alpha.11" }, diff --git a/packages/react-cosmos-plugin-vite/src/createViteRendererIndex.ts b/packages/react-cosmos-plugin-vite/src/createViteRendererIndex.ts index 726406b4d9..1979a1ba24 100644 --- a/packages/react-cosmos-plugin-vite/src/createViteRendererIndex.ts +++ b/packages/react-cosmos-plugin-vite/src/createViteRendererIndex.ts @@ -10,6 +10,9 @@ async function mount() { mountDomRenderer(args); } +// Fallback for when React Refresh isn't enabled. +// Under normal conditions accepting HMR updates here isn't necessary because +// individual React modules will be hot reloaded by React Refresh. if (import.meta.hot) { import.meta.hot.accept(() => { mount(); diff --git a/packages/react-cosmos-plugin-vite/src/viteDevServerPlugin.ts b/packages/react-cosmos-plugin-vite/src/viteDevServerPlugin.ts index 21fd0d32a3..1790905508 100644 --- a/packages/react-cosmos-plugin-vite/src/viteDevServerPlugin.ts +++ b/packages/react-cosmos-plugin-vite/src/viteDevServerPlugin.ts @@ -1,3 +1,4 @@ +import viteReactPlugin from '@vitejs/plugin-react'; import { DevServerPluginArgs, startFixtureWatcher } from 'react-cosmos'; import { createServer } from 'vite'; import { @@ -26,7 +27,7 @@ export async function viteDevServerPlugin({ host: '0.0.0.0', port: parseInt(new URL(rendererUrl).port, 10), }, - plugins: [reactCosmosViteRollupPlugin(cosmosConfig)], + plugins: [viteReactPlugin(), reactCosmosViteRollupPlugin(cosmosConfig)], }); await server.listen(); diff --git a/packages/react-cosmos-plugin-vite/src/viteExportPlugin.ts b/packages/react-cosmos-plugin-vite/src/viteExportPlugin.ts index 063fa36e3d..b00def842f 100644 --- a/packages/react-cosmos-plugin-vite/src/viteExportPlugin.ts +++ b/packages/react-cosmos-plugin-vite/src/viteExportPlugin.ts @@ -1,3 +1,4 @@ +import viteReactPlugin from '@vitejs/plugin-react'; import { rename } from 'node:fs/promises'; import path from 'path'; import { ExportPluginArgs, RENDERER_FILENAME } from 'react-cosmos'; @@ -17,7 +18,7 @@ export async function viteExportPlugin({ cosmosConfig }: ExportPluginArgs) { emptyOutDir: false, minify: false, }, - plugins: [reactCosmosViteRollupPlugin(cosmosConfig)], + plugins: [viteReactPlugin(), reactCosmosViteRollupPlugin(cosmosConfig)], }); // Make way for the Playground's index.html diff --git a/packages/react-cosmos-plugin-webpack/src/client/index.ts b/packages/react-cosmos-plugin-webpack/src/client/index.ts index 4f802059dd..4ae0bb8312 100644 --- a/packages/react-cosmos-plugin-webpack/src/client/index.ts +++ b/packages/react-cosmos-plugin-webpack/src/client/index.ts @@ -5,13 +5,10 @@ mount(); async function mount() { // Use dynamic import to reload updated modules upon hot reloading - const { rendererConfig, fixtures, decorators } = await import( - './userDeps.js' - ); + const { rendererConfig, moduleWrappers } = await import('./userDeps.js'); mountDomRenderer({ rendererConfig, - fixtures, - decorators, + moduleWrappers, onErrorReset: dismissErrorOverlay, }); } diff --git a/packages/react-cosmos-plugin-webpack/src/client/userDeps.ts b/packages/react-cosmos-plugin-webpack/src/client/userDeps.ts index 768baf8274..f5dc9299e2 100644 --- a/packages/react-cosmos-plugin-webpack/src/client/userDeps.ts +++ b/packages/react-cosmos-plugin-webpack/src/client/userDeps.ts @@ -1,12 +1,12 @@ -import { - ReactDecorators, - ReactFixtureWrappers, - RendererConfig, -} from 'react-cosmos-core'; +import { RendererConfig, UserModuleWrappers } from 'react-cosmos-core'; // NOTE: Renderer data is statically injected at compile time export const rendererConfig: RendererConfig = { playgroundUrl: 'http://localhost:5000', }; -export const fixtures: ReactFixtureWrappers = {}; -export const decorators: ReactDecorators = {}; + +export const moduleWrappers: UserModuleWrappers = { + lazy: false, + fixtures: {}, + decorators: {}, +}; diff --git a/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureButton.tsx b/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureButton.tsx index 851474c6ad..d9ce4d3260 100644 --- a/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureButton.tsx +++ b/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureButton.tsx @@ -7,7 +7,7 @@ import { FixtureTreeItem } from './FixtureTreeItem.js'; type Props = { name: string; - fixtureId: FixtureId; + fixturePath: string; indentLevel: number; selected: boolean; selectedRef: RefObject; @@ -16,14 +16,14 @@ type Props = { export function FixtureButton({ name, - fixtureId, + fixturePath, indentLevel, selected, selectedRef, onSelect, }: Props) { return ( - + { fireEvent.click(getByText(/nested/i)); expect(setExpansion).toBeCalledWith({ nested: false }); }); + +it('shows named fixture when multi fixture is selected', async () => { + const { findByText } = render( + + ); + await findByText('fuenfB'); +}); + +it('shows first named fixture when multi fixture path is selected', async () => { + const { findByText } = render( + + ); + await findByText('fuenfA'); +}); diff --git a/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureTree.tsx b/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureTree.tsx index d8c830a13e..f2b362c28b 100644 --- a/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureTree.tsx +++ b/packages/react-cosmos-ui/src/plugins/FixtureTree/FixtureTree/FixtureTree.tsx @@ -1,12 +1,8 @@ -import { isEqual } from 'lodash-es'; import React, { RefObject } from 'react'; import { FixtureId, FixtureTreeNode } from 'react-cosmos-core'; import styled from 'styled-components'; import { TreeView } from '../../../components/TreeView.js'; -import { - nodeContainsFixtureId, - recordContainsFixtureId, -} from '../../../shared/fixtureTree.js'; +import { fixtureTreeNodeContainsFixtureId } from '../../../shared/fixtureTree.js'; import { TreeExpansion } from '../../../shared/treeExpansion.js'; import { grey32 } from '../../../style/colors.js'; import { FixtureButton } from './FixtureButton.js'; @@ -40,11 +36,11 @@ export const FixtureTree = React.memo(function FixtureTree({ const { data, children } = node; if (data.type === 'fixture') { - const selected = isEqual(selectedFixtureId, data.fixtureId); + const selected = selectedFixtureId?.path === data.path; return ( ; + fixturePath: string; + fixtureNames: string[]; indentLevel: number; selected: boolean; selectedFixtureId: null | FixtureId; @@ -19,20 +19,17 @@ type Props = { export function MultiFixtureButton({ name, - fixtureIds, + fixturePath, + fixtureNames, indentLevel, selected, selectedFixtureId, selectedRef, onSelect, }: Props) { - const fixtureNames = Object.keys(fixtureIds); - const firstFixtureId = fixtureIds[fixtureNames[0]]; - if (!firstFixtureId) return null; - if (!selected) return ( - + {name} {fixtureNames.length} @@ -46,9 +43,17 @@ export function MultiFixtureButton({ {name} {fixtureNames.length} - {fixtureNames.map(fixtureName => { - const fixtureId = fixtureIds[fixtureName]; - const childSelected = isEqual(fixtureId, selectedFixtureId); + {fixtureNames.map((fixtureName, index) => { + const fixtureId = { path: fixturePath, name: fixtureName }; + + // Select first child when only the path of a multi fixture is selected + const childSelected = + selectedFixtureId !== null && + selectedFixtureId.path === fixturePath && + (selectedFixtureId.name === undefined + ? index === 0 + : fixtureName === selectedFixtureId.name); + return ( { expect(isValidFixtureSelected()).toBe(true); }); + +it('returns true on existing multi fixture without name', async () => { + mockFixtureId({ path: 'ein.js' }); + loadTestPlugins(); + + expect(isValidFixtureSelected()).toBe(true); +}); diff --git a/packages/react-cosmos-ui/src/plugins/RendererCore/isValidFixtureSelected.ts b/packages/react-cosmos-ui/src/plugins/RendererCore/isValidFixtureSelected.ts index 8d53ee58b6..15bf8821a5 100644 --- a/packages/react-cosmos-ui/src/plugins/RendererCore/isValidFixtureSelected.ts +++ b/packages/react-cosmos-ui/src/plugins/RendererCore/isValidFixtureSelected.ts @@ -13,8 +13,11 @@ export function isValidFixtureSelected(context: RendererCoreContext) { } const fixtureItem = fixtures[fixtureId.path]; - return fixtureItem.type === 'multi' - ? fixtureId.name !== undefined && - fixtureItem.fixtureNames.indexOf(fixtureId.name) !== -1 - : fixtureId.name === undefined; + return ( + (fixtureItem.type === 'single' && fixtureId.name === undefined) || + // Allow selecting multi fixtures by path only + (fixtureItem.type === 'multi' && + (fixtureId.name === undefined || + fixtureItem.fixtureNames.indexOf(fixtureId.name) !== -1)) + ); } diff --git a/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/fixtureListItemUpdate.ts b/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/fixtureListItemUpdate.ts new file mode 100644 index 0000000000..273ead95aa --- /dev/null +++ b/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/fixtureListItemUpdate.ts @@ -0,0 +1,21 @@ +import { FixtureListItemUpdateResponse } from 'react-cosmos-core'; +import { RendererCoreContext } from '../shared/index.js'; + +export function receiveFixtureListItemUpdateResponse( + context: RendererCoreContext, + { payload }: FixtureListItemUpdateResponse +) { + const { rendererId, fixturePath, fixtureItem } = payload; + const { primaryRendererId } = context.getState(); + + // Discard updates from secondary renderers + if (rendererId === primaryRendererId) { + context.setState(prevState => ({ + ...prevState, + fixtures: { + ...prevState.fixtures, + [fixturePath]: fixtureItem, + }, + })); + } +} diff --git a/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/index.ts b/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/index.ts index 28cbb1918e..e5ed0dde0e 100644 --- a/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/index.ts +++ b/packages/react-cosmos-ui/src/plugins/RendererCore/receiveResponse/index.ts @@ -1,5 +1,6 @@ import { MessageType, RendererResponse } from 'react-cosmos-core'; import { RendererCoreContext } from '../shared/index.js'; +import { receiveFixtureListItemUpdateResponse } from './fixtureListItemUpdate.js'; import { receiveFixtureListUpdateResponse } from './fixtureListUpdate.js'; import { receiveFixtureStateChangeResponse } from './fixtureStateChange.js'; import { receivePlaygroundCommandResponse } from './playgroundCommand.js'; @@ -17,6 +18,8 @@ export function receiveResponse( return receiveRendererReadyResponse(context, rendererResponse); case 'fixtureListUpdate': return receiveFixtureListUpdateResponse(context, rendererResponse); + case 'fixtureListItemUpdate': + return receiveFixtureListItemUpdateResponse(context, rendererResponse); case 'fixtureStateChange': return receiveFixtureStateChangeResponse(context, rendererResponse); case 'playgroundCommand': diff --git a/packages/react-cosmos-ui/src/plugins/Root/RendererHeader.tsx b/packages/react-cosmos-ui/src/plugins/Root/RendererHeader.tsx index b2827cba76..1952521371 100644 --- a/packages/react-cosmos-ui/src/plugins/Root/RendererHeader.tsx +++ b/packages/react-cosmos-ui/src/plugins/Root/RendererHeader.tsx @@ -95,8 +95,16 @@ function findFixtureItemById( fixtureItems: FlatFixtureTreeItem[], fixtureId: FixtureId ) { - return fixtureItems.find(fixtureItem => - isEqual(fixtureItem.fixtureId, fixtureId) + if (fixtureId.name) { + return fixtureItems.find(fixtureItem => + isEqual(fixtureItem.fixtureId, fixtureId) + ); + } + + // When a multi fixture is selected by path only, the first of its named + // fixtures will be selected. + return fixtureItems.find( + fixtureItem => fixtureItem.fixtureId.path === fixtureId.path ); } diff --git a/packages/react-cosmos-ui/src/shared/fixtureTree.ts b/packages/react-cosmos-ui/src/shared/fixtureTree.ts index ea9505a225..5712618d4e 100644 --- a/packages/react-cosmos-ui/src/shared/fixtureTree.ts +++ b/packages/react-cosmos-ui/src/shared/fixtureTree.ts @@ -1,28 +1,17 @@ -import { isEqual } from 'lodash-es'; import { FixtureId, FixtureTreeNode } from 'react-cosmos-core'; -export function recordContainsFixtureId( - fixtureIds: Record, - fixtureId: FixtureId -) { - return Object.keys(fixtureIds).some(fixtureName => - isEqual(fixtureIds[fixtureName], fixtureId) - ); -} - -export function nodeContainsFixtureId( +export function fixtureTreeNodeContainsFixtureId( { data, children }: FixtureTreeNode, fixtureId: FixtureId ): boolean { - if (data.type === 'fixture') return isEqual(data.fixtureId, fixtureId); - - if (data.type === 'multiFixture') - return recordContainsFixtureId(data.fixtureIds, fixtureId); + if (data.type === 'fixture' || data.type === 'multiFixture') { + return data.path === fixtureId.path; + } return ( children !== undefined && Object.keys(children).some(childName => - nodeContainsFixtureId(children[childName], fixtureId) + fixtureTreeNodeContainsFixtureId(children[childName], fixtureId) ) ); } diff --git a/packages/react-cosmos/config.schema.json b/packages/react-cosmos/config.schema.json index 76eae3c9da..13c3c2926f 100644 --- a/packages/react-cosmos/config.schema.json +++ b/packages/react-cosmos/config.schema.json @@ -65,6 +65,10 @@ }, "uniqueItems": true }, + "lazy": { + "description": "Dynamically import fixture and decorator modules as they are loaded. When false all fixture and decorator modules are imported statically and bundled together. [default: false]", + "type": "boolean" + }, "userDepsFilePath": { "description": "Where to generate a file that contains a map to all fixtures and other Cosmos-related user modules. Only used in setups where Cosmos can't piggyback on the user's build (eg. React Native setups). [default: \"cosmos.userdeps.js\"]", "type": "string", @@ -83,11 +87,11 @@ "type": "number" }, "https": { - "description": "Server will be served over HTTPS", + "description": "Server will be served over HTTPS.", "type": "boolean" }, "httpsOptions": { - "description": "Additional options for HTTPS server", + "description": "Additional options for HTTPS server.", "type": "object", "additionalProperties": false, "properties": { diff --git a/packages/react-cosmos/src/cosmosConfig/createCosmosConfig.ts b/packages/react-cosmos/src/cosmosConfig/createCosmosConfig.ts index a71b3a9057..695136b75e 100644 --- a/packages/react-cosmos/src/cosmosConfig/createCosmosConfig.ts +++ b/packages/react-cosmos/src/cosmosConfig/createCosmosConfig.ts @@ -24,6 +24,7 @@ export function createCosmosConfig( https: getHttps(cosmosConfigInput), httpsOptions: getHttpsOptions(cosmosConfigInput, rootDir), ignore: getIgnore(cosmosConfigInput), + lazy: getLazy(cosmosConfigInput), port: getPort(cosmosConfigInput), portRetries: getPortRetries(cosmosConfigInput), plugins: getPlugins(cosmosConfigInput, rootDir), @@ -136,3 +137,13 @@ function getDomConfig(cosmosConfigInput: CosmosDomConfigInput) { containerQuerySelector, }; } + +function getLazy(cosmosConfigInput: CosmosConfigInput) { + const cliArgs = getCliArgs(); + if (typeof cliArgs.lazy === 'boolean') { + return cliArgs.lazy; + } + + const { lazy = false } = cosmosConfigInput; + return lazy; +} diff --git a/packages/react-cosmos/src/cosmosConfig/types.ts b/packages/react-cosmos/src/cosmosConfig/types.ts index 2025afda79..596bcec45e 100644 --- a/packages/react-cosmos/src/cosmosConfig/types.ts +++ b/packages/react-cosmos/src/cosmosConfig/types.ts @@ -24,6 +24,7 @@ export type CosmosConfig = { https: boolean; httpsOptions: null | HttpsOptions; ignore: string[]; + lazy: boolean; port: number; portRetries: number; plugins: string[]; diff --git a/packages/react-cosmos/src/getFixtures/getFixtures.ts b/packages/react-cosmos/src/getFixtures/getFixtures.ts index 0adb98c416..504ea88073 100644 --- a/packages/react-cosmos/src/getFixtures/getFixtures.ts +++ b/packages/react-cosmos/src/getFixtures/getFixtures.ts @@ -1,6 +1,7 @@ import path from 'path'; import { ReactElement } from 'react'; import { + ByPath, createFixtureTree, FixtureId, flattenFixtureTree, @@ -8,7 +9,6 @@ import { getFixtureListFromExports, getSortedDecoratorsForFixturePath, ReactDecorator, - ReactDecorators, ReactFixture, ReactFixtureMap, stringifyPlaygroundUrlQuery, @@ -96,7 +96,7 @@ function getPlaygroundHost({ hostname, port, https }: CosmosConfig) { function createFixtureElementGetter( fixture: ReactFixture, fixturePath: string, - decoratorsByPath: ReactDecorators + decoratorsByPath: ByPath ): () => ReactElement { const decorators: ReactDecorator[] = getSortedDecoratorsForFixturePath( fixturePath, diff --git a/packages/react-cosmos/src/testHelpers/mockYargs.ts b/packages/react-cosmos/src/testHelpers/mockYargs.ts index fede571fca..9b480f8a79 100644 --- a/packages/react-cosmos/src/testHelpers/mockYargs.ts +++ b/packages/react-cosmos/src/testHelpers/mockYargs.ts @@ -4,6 +4,7 @@ jest.mock('yargs/yargs', () => { let argv = {}; const yargs = () => ({ + boolean: () => yargs(), parseSync: () => argv, }); diff --git a/packages/react-cosmos/src/userDeps/__snapshots__/userDepsLazyTemplate.test.ts.snap b/packages/react-cosmos/src/userDeps/__snapshots__/userDepsLazyTemplate.test.ts.snap new file mode 100644 index 0000000000..2010efa808 --- /dev/null +++ b/packages/react-cosmos/src/userDeps/__snapshots__/userDepsLazyTemplate.test.ts.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should generate user deps module with absolute paths 1`] = ` +"// This file is automatically generated by Cosmos. Add it to .gitignore and +// only edit if you know what you're doing. + +// Keeping global imports here is superior to making them bundle entry points +// because this way they become hot-reloadable. +import '/Users/ovidiu/cosmos/src/polyfills.ts'; +import '/Users/ovidiu/cosmos/src/global.css'; + +export const rendererConfig = { + "playgroundUrl": "http://localhost:5002" +}; + +const fixtures = { + 'src/Counter/Counter.fixture.tsx': { getModule: () => import('/Users/ovidiu/cosmos/src/Counter/Counter.fixture.tsx') }, + 'src/CounterButton/CounterButton.fixture.tsx': { getModule: () => import('/Users/ovidiu/cosmos/src/CounterButton/CounterButton.fixture.tsx') } +}; + +const decorators = { + 'src/cosmos.decorator.tsx': { getModule: () => import('/Users/ovidiu/cosmos/src/cosmos.decorator.tsx') } +}; + +export const moduleWrappers = { + lazy: true, + fixtures, + decorators, +}; +" +`; + +exports[`should generate user deps module with relative paths 1`] = ` +"// This file is automatically generated by Cosmos. Add it to .gitignore and +// only edit if you know what you're doing. + +// Keeping global imports here is superior to making them bundle entry points +// because this way they become hot-reloadable. +import './polyfills.ts'; +import './global.css'; + +export const rendererConfig = { + "playgroundUrl": "http://localhost:5002" +}; + +const fixtures = { + 'src/Counter/Counter.fixture.tsx': { getModule: () => import('./Counter/Counter.fixture.tsx') }, + 'src/CounterButton/CounterButton.fixture.tsx': { getModule: () => import('./CounterButton/CounterButton.fixture.tsx') } +}; + +const decorators = { + 'src/cosmos.decorator.tsx': { getModule: () => import('./cosmos.decorator.tsx') } +}; + +export const moduleWrappers = { + lazy: true, + fixtures, + decorators, +}; +" +`; diff --git a/packages/react-cosmos/src/userDeps/__snapshots__/userDepsTemplate.test.ts.snap b/packages/react-cosmos/src/userDeps/__snapshots__/userDepsTemplate.test.ts.snap index cfa717e04d..4429c2783e 100644 --- a/packages/react-cosmos/src/userDeps/__snapshots__/userDepsTemplate.test.ts.snap +++ b/packages/react-cosmos/src/userDeps/__snapshots__/userDepsTemplate.test.ts.snap @@ -18,13 +18,19 @@ export const rendererConfig = { "playgroundUrl": "http://localhost:5002" }; -export const fixtures = { - 'src/Counter/Counter.fixture.tsx': { module: { default: fixture0 } }, - 'src/CounterButton/CounterButton.fixture.tsx': { module: { default: fixture1 } } +const fixtures = { + 'src/Counter/Counter.fixture.tsx': { module: { default: fixture0 } }, + 'src/CounterButton/CounterButton.fixture.tsx': { module: { default: fixture1 } } }; -export const decorators = { - 'src/cosmos.decorator.tsx': decorator0 +const decorators = { + 'src/cosmos.decorator.tsx': { module: { default: decorator0 } } +}; + +export const moduleWrappers = { + lazy: false, + fixtures, + decorators, }; " `; @@ -47,13 +53,19 @@ export const rendererConfig = { "playgroundUrl": "http://localhost:5002" }; -export const fixtures = { - 'src/Counter/Counter.fixture.tsx': { module: { default: fixture0 } }, - 'src/CounterButton/CounterButton.fixture.tsx': { module: { default: fixture1 } } +const fixtures = { + 'src/Counter/Counter.fixture.tsx': { module: { default: fixture0 } }, + 'src/CounterButton/CounterButton.fixture.tsx': { module: { default: fixture1 } } +}; + +const decorators = { + 'src/cosmos.decorator.tsx': { module: { default: decorator0 } } }; -export const decorators = { - 'src/cosmos.decorator.tsx': decorator0 +export const moduleWrappers = { + lazy: false, + fixtures, + decorators, }; " `; diff --git a/packages/react-cosmos/src/userDeps/generateUserDepsModule.ts b/packages/react-cosmos/src/userDeps/generateUserDepsModule.ts index 87140bf1ab..b3cbbe87de 100644 --- a/packages/react-cosmos/src/userDeps/generateUserDepsModule.ts +++ b/packages/react-cosmos/src/userDeps/generateUserDepsModule.ts @@ -1,6 +1,7 @@ import { CosmosConfig } from '../cosmosConfig/types.js'; import { findUserModulePaths } from './findUserModulePaths.js'; import { Json } from './shared.js'; +import { userDepsLazyTemplate } from './userDepsLazyTemplate.js'; import { userDepsTemplate } from './userDepsTemplate.js'; type Args = { @@ -21,7 +22,10 @@ export function generateUserDepsModule({ fixtureFileSuffix, ignore, }); - return userDepsTemplate({ + + const template = cosmosConfig.lazy ? userDepsLazyTemplate : userDepsTemplate; + + return template({ globalImports, fixturePaths, decoratorPaths, diff --git a/packages/react-cosmos/src/userDeps/getUserModules.ts b/packages/react-cosmos/src/userDeps/getUserModules.ts index dad49aff28..f3ad5fef38 100644 --- a/packages/react-cosmos/src/userDeps/getUserModules.ts +++ b/packages/react-cosmos/src/userDeps/getUserModules.ts @@ -1,17 +1,12 @@ import path from 'path'; -import { - ReactDecorator, - ReactDecorators, - ReactFixtureExport, - ReactFixtureExports, -} from 'react-cosmos-core'; +import { ByPath, ReactDecorator, ReactFixtureExport } from 'react-cosmos-core'; import { CosmosConfig } from '../cosmosConfig/types.js'; import { slash } from '../utils/slash.js'; import { findUserModulePaths } from './findUserModulePaths.js'; type UserModules = { - fixtures: ReactFixtureExports; - decorators: ReactDecorators; + fixtures: ByPath; + decorators: ByPath; }; export function getUserModules({ diff --git a/packages/react-cosmos/src/userDeps/userDepsLazyTemplate.test.ts b/packages/react-cosmos/src/userDeps/userDepsLazyTemplate.test.ts new file mode 100644 index 0000000000..cbaa840646 --- /dev/null +++ b/packages/react-cosmos/src/userDeps/userDepsLazyTemplate.test.ts @@ -0,0 +1,44 @@ +import { RendererConfig } from 'react-cosmos-core'; +import { userDepsLazyTemplate } from './userDepsLazyTemplate.js'; + +const globalImports = [ + '/Users/ovidiu/cosmos/src/polyfills.ts', + '/Users/ovidiu/cosmos/src/global.css', +]; + +const fixturePaths = [ + '/Users/ovidiu/cosmos/src/Counter/Counter.fixture.tsx', + '/Users/ovidiu/cosmos/src/CounterButton/CounterButton.fixture.tsx', +]; + +const decoratorPaths = ['/Users/ovidiu/cosmos/src/cosmos.decorator.tsx']; + +const rendererConfig: RendererConfig = { + playgroundUrl: 'http://localhost:5002', +}; + +it('should generate user deps module with absolute paths', () => { + expect( + userDepsLazyTemplate({ + globalImports, + fixturePaths, + decoratorPaths, + rendererConfig, + rootDir: '/Users/ovidiu/cosmos/', + relativeToDir: null, + }) + ).toMatchSnapshot(); +}); + +it('should generate user deps module with relative paths', () => { + expect( + userDepsLazyTemplate({ + globalImports, + fixturePaths, + decoratorPaths, + rendererConfig, + rootDir: '/Users/ovidiu/cosmos', + relativeToDir: '/Users/ovidiu/cosmos/src', + }) + ).toMatchSnapshot(); +}); diff --git a/packages/react-cosmos/src/userDeps/userDepsLazyTemplate.ts b/packages/react-cosmos/src/userDeps/userDepsLazyTemplate.ts new file mode 100644 index 0000000000..b6c71a61fc --- /dev/null +++ b/packages/react-cosmos/src/userDeps/userDepsLazyTemplate.ts @@ -0,0 +1,51 @@ +import { + userDepsImportMap, + userDepsImportPath, + UserDepsTemplateArgs, +} from './userDepsShared.js'; + +export function userDepsLazyTemplate({ + globalImports, + fixturePaths, + decoratorPaths, + rendererConfig, + rootDir, + relativeToDir, +}: UserDepsTemplateArgs) { + const fixtures = userDepsImportMap(fixturePaths, rootDir, relativeToDir); + const fixtureKeys = Object.keys(fixtures); + + const decorators = userDepsImportMap(decoratorPaths, rootDir, relativeToDir); + const decoratorKeys = Object.keys(decorators); + + return ` +// This file is automatically generated by Cosmos. Add it to .gitignore and +// only edit if you know what you're doing. + +// Keeping global imports here is superior to making them bundle entry points +// because this way they become hot-reloadable. +${globalImports + .map(p => `import '${userDepsImportPath(p, relativeToDir)}';`) + .join(`\n`)} + +export const rendererConfig = ${JSON.stringify(rendererConfig, null, 2)}; + +const fixtures = { +${fixtureKeys + .map(k => ` '${k}': { getModule: () => import('${fixtures[k]}') }`) + .join(`,\n`)} +}; + +const decorators = { +${decoratorKeys + .map(k => ` '${k}': { getModule: () => import('${decorators[k]}') }`) + .join(`,\n`)} +}; + +export const moduleWrappers = { + lazy: true, + fixtures, + decorators, +}; +`.trimStart(); +} diff --git a/packages/react-cosmos/src/userDeps/userDepsShared.ts b/packages/react-cosmos/src/userDeps/userDepsShared.ts new file mode 100644 index 0000000000..01157b0eef --- /dev/null +++ b/packages/react-cosmos/src/userDeps/userDepsShared.ts @@ -0,0 +1,37 @@ +import path from 'path'; +import { slash } from '../utils/slash.js'; +import { Json } from './shared.js'; + +export type UserDepsTemplateArgs = { + globalImports: string[]; + fixturePaths: string[]; + decoratorPaths: string[]; + rendererConfig: Json; + rootDir: string; + relativeToDir: string | null; +}; + +export function userDepsImportMap( + paths: string[], + rootDir: string, + relativeToDir: string | null +): Record { + return paths.reduce( + (acc, p) => ({ + ...acc, + [slash(path.relative(rootDir, p))]: userDepsImportPath(p, relativeToDir), + }), + {} + ); +} + +export function userDepsImportPath( + filePath: string, + relativeToDir: string | null +) { + return slash( + relativeToDir + ? `.${path.sep}${path.relative(relativeToDir, filePath)}` + : filePath + ); +} diff --git a/packages/react-cosmos/src/userDeps/userDepsTemplate.ts b/packages/react-cosmos/src/userDeps/userDepsTemplate.ts index bf3c0565e8..621eda6a20 100644 --- a/packages/react-cosmos/src/userDeps/userDepsTemplate.ts +++ b/packages/react-cosmos/src/userDeps/userDepsTemplate.ts @@ -1,15 +1,9 @@ -import path from 'path'; -import { slash } from '../utils/slash.js'; -import { Json } from './shared.js'; +import { + userDepsImportMap, + userDepsImportPath, + UserDepsTemplateArgs, +} from './userDepsShared.js'; -type Args = { - globalImports: string[]; - fixturePaths: string[]; - decoratorPaths: string[]; - rendererConfig: Json; - rootDir: string; - relativeToDir: string | null; -}; export function userDepsTemplate({ globalImports, fixturePaths, @@ -17,11 +11,11 @@ export function userDepsTemplate({ rendererConfig, rootDir, relativeToDir, -}: Args) { - const fixtures = createImportMap(fixturePaths, rootDir, relativeToDir); +}: UserDepsTemplateArgs) { + const fixtures = userDepsImportMap(fixturePaths, rootDir, relativeToDir); const fixtureKeys = Object.keys(fixtures); - const decorators = createImportMap(decoratorPaths, rootDir, relativeToDir); + const decorators = userDepsImportMap(decoratorPaths, rootDir, relativeToDir); const decoratorKeys = Object.keys(decorators); return ` @@ -31,7 +25,7 @@ export function userDepsTemplate({ // Keeping global imports here is superior to making them bundle entry points // because this way they become hot-reloadable. ${globalImports - .map(p => `import '${resolveImportPath(p, relativeToDir)}';`) + .map(p => `import '${userDepsImportPath(p, relativeToDir)}';`) .join(`\n`)} ${fixtureKeys @@ -44,36 +38,22 @@ ${decoratorKeys export const rendererConfig = ${JSON.stringify(rendererConfig, null, 2)}; -export const fixtures = { +const fixtures = { ${fixtureKeys - .map((k, i) => ` '${k}': { module: { default: fixture${i} } }`) + .map((k, i) => ` '${k}': { module: { default: fixture${i} } }`) .join(`,\n`)} }; -export const decorators = { -${decoratorKeys.map((k, i) => ` '${k}': decorator${i}`).join(`,\n`)} +const decorators = { +${decoratorKeys + .map((k, i) => ` '${k}': { module: { default: decorator${i} } }`) + .join(`,\n`)} }; -`.trimStart(); -} -function createImportMap( - paths: string[], - rootDir: string, - relativeToDir: string | null -): Record { - return paths.reduce( - (acc, p) => ({ - ...acc, - [slash(path.relative(rootDir, p))]: resolveImportPath(p, relativeToDir), - }), - {} - ); -} - -function resolveImportPath(filePath: string, relativeToDir: string | null) { - return slash( - relativeToDir - ? `.${path.sep}${path.relative(relativeToDir, filePath)}` - : filePath - ); +export const moduleWrappers = { + lazy: false, + fixtures, + decorators, +}; +`.trimStart(); } diff --git a/packages/react-cosmos/src/utils/cli.ts b/packages/react-cosmos/src/utils/cli.ts index 8c4775ce09..54c80d2dc5 100644 --- a/packages/react-cosmos/src/utils/cli.ts +++ b/packages/react-cosmos/src/utils/cli.ts @@ -1,5 +1,5 @@ import yargs from 'yargs/yargs'; export function getCliArgs() { - return yargs(process.argv.slice(2)).parseSync(); + return yargs(process.argv.slice(2)).boolean('lazy').parseSync(); } diff --git a/yarn.lock b/yarn.lock index 77d68196c4..7ec98f312c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,6 +22,13 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/compat-data@^7.20.0": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" @@ -32,6 +39,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== +"@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== + "@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" @@ -53,6 +65,27 @@ json5 "^2.2.1" semver "^6.3.0" +"@babel/core@^7.20.12": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + "@babel/core@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" @@ -93,6 +126,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -121,6 +164,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" @@ -205,6 +259,20 @@ "@babel/traverse" "^7.21.0" "@babel/types" "^7.21.0" +"@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -312,6 +380,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.1.tgz#a8f81ee2fe872af23faea4b17a08fcc869de7bcc" integrity sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg== +"@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -424,6 +497,20 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.18.6" +"@babel/plugin-transform-react-jsx-self@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" + integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-react-jsx-source@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" + integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-transform-react-jsx@^7.18.6": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" @@ -530,6 +617,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" @@ -557,6 +660,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.21.2", "@babel/types@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1056,6 +1168,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.13": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -2140,6 +2257,17 @@ "@typescript-eslint/types" "5.54.1" eslint-visitor-keys "^3.3.0" +"@vitejs/plugin-react@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz#d1091f535eab8b83d6e74034d01e27d73c773240" + integrity sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g== + dependencies: + "@babel/core" "^7.20.12" + "@babel/plugin-transform-react-jsx-self" "^7.18.6" + "@babel/plugin-transform-react-jsx-source" "^7.19.6" + magic-string "^0.27.0" + react-refresh "^0.14.0" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -6765,6 +6893,13 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -8225,6 +8360,11 @@ react-plugin@^3.0.0-alpha.4: lodash "^4.17.21" ui-plugin "^3.0.0-alpha.5" +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + react-shallow-renderer@^16.15.0: version "16.15.0" resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"