From 29f8596e326a8ae6c501e965afd4dd8f87aa3f8a Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 17 Aug 2023 16:27:08 +0200 Subject: [PATCH 01/36] Create @storybook/test package --- code/addons/actions/src/addArgs.ts | 7 +- code/addons/actions/src/addArgsHelpers.ts | 31 +++ code/lib/test/jest.config.js | 7 + code/lib/test/package.json | 68 ++++++ code/lib/test/project.json | 6 + code/lib/test/src/index.ts | 32 +++ code/lib/test/tsconfig.json | 4 + code/yarn.lock | 242 +++++++++++++++++++++- 8 files changed, 390 insertions(+), 7 deletions(-) create mode 100644 code/lib/test/jest.config.js create mode 100644 code/lib/test/package.json create mode 100644 code/lib/test/project.json create mode 100644 code/lib/test/src/index.ts create mode 100644 code/lib/test/tsconfig.json diff --git a/code/addons/actions/src/addArgs.ts b/code/addons/actions/src/addArgs.ts index 5742bd8627c0..db14aee0ce3d 100644 --- a/code/addons/actions/src/addArgs.ts +++ b/code/addons/actions/src/addArgs.ts @@ -1,7 +1,12 @@ import type { ArgsEnhancer } from '@storybook/types'; -import { addActionsFromArgTypes, inferActionsFromArgTypesRegex } from './addArgsHelpers'; +import { + addActionsFromArgTypes, + attachActionsToFunctionMocks, + inferActionsFromArgTypesRegex, +} from './addArgsHelpers'; export const argsEnhancers: ArgsEnhancer[] = [ addActionsFromArgTypes, inferActionsFromArgTypesRegex, + attachActionsToFunctionMocks, ]; diff --git a/code/addons/actions/src/addArgsHelpers.ts b/code/addons/actions/src/addArgsHelpers.ts index 7f56922d3962..fd93bea6e6f0 100644 --- a/code/addons/actions/src/addArgsHelpers.ts +++ b/code/addons/actions/src/addArgsHelpers.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle,no-param-reassign */ import type { Args, Renderer, ArgsEnhancer } from '@storybook/types'; import { action } from './runtime/action'; @@ -61,3 +62,33 @@ export const addActionsFromArgTypes: ArgsEnhancer = (context) => { return acc; }, {} as Args); }; + +export const attachActionsToFunctionMocks: ArgsEnhancer = (context) => { + const { + initialArgs, + argTypes, + parameters: { actions }, + } = context; + if (actions?.disable || !argTypes) { + return {}; + } + + const argTypesWithAction = Object.entries(initialArgs).filter( + ([, value]) => + typeof value === 'function' && + '_isMockFunction' in value && + value._isMockFunction && + !value._actionAttached + ); + + return argTypesWithAction.reduce((acc, [key, value]) => { + const previous = value.getMockImplementation(); + value.mockImplementation((...args: unknown[]) => { + action(key)(...args); + return previous?.(...args); + }); + // this enhancer is being called multiple times + value._actionAttached = true; + return acc; + }, {} as Args); +}; diff --git a/code/lib/test/jest.config.js b/code/lib/test/jest.config.js new file mode 100644 index 000000000000..4396fbc7010d --- /dev/null +++ b/code/lib/test/jest.config.js @@ -0,0 +1,7 @@ +const path = require('path'); +const baseConfig = require('../../jest.config.browser'); + +module.exports = { + ...baseConfig, + displayName: __dirname.split(path.sep).slice(-2).join(path.posix.sep), +}; diff --git a/code/lib/test/package.json b/code/lib/test/package.json new file mode 100644 index 000000000000..92d9ed1995ea --- /dev/null +++ b/code/lib/test/package.json @@ -0,0 +1,68 @@ +{ + "name": "@storybook/test", + "version": "7.4.0-alpha.0", + "description": "", + "keywords": [ + "storybook" + ], + "homepage": "https://github.com/storybookjs/storybook/tree/next/code/lib/test", + "bugs": { + "url": "https://github.com/storybookjs/storybook/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/storybookjs/storybook.git", + "directory": "code/lib/test" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "license": "MIT", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./dist/index.js", + "require": "./dist/index.js", + "import": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist/**/*", + "README.md", + "*.js", + "*.d.ts" + ], + "scripts": { + "check": "../../../scripts/prepare/check.ts", + "prep": "../../../scripts/prepare/bundle.ts" + }, + "dependencies": { + "@storybook/instrumenter": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@vitest/expect": "^0.34.2", + "@vitest/spy": "^0.34.1", + "chai": "^4.3.7", + "expect": "^29.6.2" + }, + "devDependencies": { + "@types/chai": "^4", + "@types/testing-library__jest-dom": "^6", + "type-fest": "~2.19", + "typescript": "~4.9.3" + }, + "publishConfig": { + "access": "public" + }, + "bundler": { + "entries": [ + "./src/index.ts" + ] + }, + "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17" +} diff --git a/code/lib/test/project.json b/code/lib/test/project.json new file mode 100644 index 000000000000..68c18c664fd3 --- /dev/null +++ b/code/lib/test/project.json @@ -0,0 +1,6 @@ +{ + "name": "@storybook/test", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "implicitDependencies": [], + "type": "library" +} diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts new file mode 100644 index 000000000000..4d44c09ba333 --- /dev/null +++ b/code/lib/test/src/index.ts @@ -0,0 +1,32 @@ +/* eslint-disable import/no-extraneous-dependencies,import/no-named-default */ +import { default as expectPatched } from '@storybook/expect'; +import { instrument } from '@storybook/instrumenter'; +import * as matchers from '@testing-library/jest-dom/matchers'; +import * as _mock from '@vitest/spy'; +export type * from '@vitest/spy'; + +export const { mock } = instrument({ mock: _mock }, { retain: true }); + +/** + * The `expect` function is used every time you want to test a value. + * You will rarely call `expect` by itself. + */ +export interface Expect extends Pick { + /** + * The `expect` function is used every time you want to test a value. + * You will rarely call `expect` by itself. + * + * @param actual The value to apply matchers against. + */ + (actual: T): jest.JestMatchersShape< + jest.Matchers, T>, + jest.Matchers, T> + >; +} + +expectPatched.extend(matchers); + +export const expect: Expect = instrument( + { expect: expectPatched }, + { intercept: (_method, path) => path[0] !== 'expect' } +).expect as unknown as Expect; diff --git a/code/lib/test/tsconfig.json b/code/lib/test/tsconfig.json new file mode 100644 index 000000000000..52d43eaaa9b9 --- /dev/null +++ b/code/lib/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"] +} diff --git a/code/yarn.lock b/code/yarn.lock index a9ad7bd0ce70..284c1a3103d0 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -3570,6 +3570,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/expect-utils@npm:29.6.2" + dependencies: + jest-get-type: ^29.4.3 + checksum: 9a8dd5197d7e2d7f25a697dcb26dcf1483c5a9eccd1637ebd95b6deded6dd6d7d5513ceb1295119cd979abfc78d814ed00ff118a752028adb6646231ee234086 + languageName: node + linkType: hard + "@jest/expect-utils@npm:^29.7.0": version: 29.7.0 resolution: "@jest/expect-utils@npm:29.7.0" @@ -3661,7 +3670,7 @@ __metadata: languageName: node linkType: hard -"@jest/schemas@npm:^29.4.3, @jest/schemas@npm:^29.6.3": +"@jest/schemas@npm:^29.4.3, @jest/schemas@npm:^29.6.0, @jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" dependencies: @@ -3754,7 +3763,7 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^29.6.3": +"@jest/types@npm:^29.6.1, @jest/types@npm:^29.6.3": version: 29.6.3 resolution: "@jest/types@npm:29.6.3" dependencies: @@ -7148,7 +7157,7 @@ __metadata: languageName: node linkType: hard -"@storybook/instrumenter@workspace:*, @storybook/instrumenter@workspace:lib/instrumenter": +"@storybook/instrumenter@workspace:*, @storybook/instrumenter@workspace:^, @storybook/instrumenter@workspace:lib/instrumenter": version: 0.0.0-use.local resolution: "@storybook/instrumenter@workspace:lib/instrumenter" dependencies: @@ -8138,6 +8147,23 @@ __metadata: languageName: unknown linkType: soft +"@storybook/test@workspace:lib/test": + version: 0.0.0-use.local + resolution: "@storybook/test@workspace:lib/test" + dependencies: + "@storybook/instrumenter": "workspace:^" + "@testing-library/jest-dom": ^6.0.0 + "@types/chai": ^4 + "@types/testing-library__jest-dom": ^6 + "@vitest/expect": ^0.34.2 + "@vitest/spy": ^0.34.1 + chai: ^4.3.7 + expect: ^29.6.2 + type-fest: ~2.19 + typescript: ~4.9.3 + languageName: unknown + linkType: soft + "@storybook/testing-library@npm:next": version: 0.2.1-next.0 resolution: "@storybook/testing-library@npm:0.2.1-next.0" @@ -8959,6 +8985,13 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^4": + version: 4.3.5 + resolution: "@types/chai@npm:4.3.5" + checksum: 816b3081e067c6e332be313e2e9a518b117c9eac51201cac749de0d6fcf7cb3238d0def37690b6539b3a928bd87b2f5f777914248447889ebc6f630a0d00e0e5 + languageName: node + linkType: hard + "@types/cheerio@npm:^0.22.22": version: 0.22.32 resolution: "@types/cheerio@npm:0.22.32" @@ -9782,6 +9815,15 @@ __metadata: languageName: node linkType: hard +"@types/testing-library__jest-dom@npm:^6": + version: 6.0.0 + resolution: "@types/testing-library__jest-dom@npm:6.0.0" + dependencies: + "@testing-library/jest-dom": "*" + checksum: 824950dc82752ddb656fa7ca851bf454804332f7a7a8571231abfc3553902198c6b96de209bb3dd1f1a15ee6a269a1efa56866041f5692ee0129308772e658e0 + languageName: node + linkType: hard + "@types/tmp@npm:^0.2.3": version: 0.2.3 resolution: "@types/tmp@npm:0.2.3" @@ -10137,6 +10179,46 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:^0.34.2": + version: 0.34.2 + resolution: "@vitest/expect@npm:0.34.2" + dependencies: + "@vitest/spy": 0.34.2 + "@vitest/utils": 0.34.2 + chai: ^4.3.7 + checksum: 80119614a826996a36f861e7b41cc41771009e1c6d1fcfa5c024d9f5aba83c2731058dc6869fef31e29631c68bec939c8373c4905c0612e860829fca616b76dc + languageName: node + linkType: hard + +"@vitest/spy@npm:0.34.2": + version: 0.34.2 + resolution: "@vitest/spy@npm:0.34.2" + dependencies: + tinyspy: ^2.1.1 + checksum: 98fbcea189d917693321cfded1b11d80ea8f1c1f6c6332ccbacb365e68640c286ecce28b563bb0b786e807d57f9cdd62b845ecf92e88bf184b38034ee85e4c69 + languageName: node + linkType: hard + +"@vitest/spy@npm:^0.34.1": + version: 0.34.1 + resolution: "@vitest/spy@npm:0.34.1" + dependencies: + tinyspy: ^2.1.1 + checksum: ba554f34276df580613027785ab8ca02cfd31cb588b0c506341e9a4aa75e369eaaf9d991119aa11b28e4b66819e659a3dbb4afacf46b387daabad1072746358f + languageName: node + linkType: hard + +"@vitest/utils@npm:0.34.2": + version: 0.34.2 + resolution: "@vitest/utils@npm:0.34.2" + dependencies: + diff-sequences: ^29.4.3 + loupe: ^2.3.6 + pretty-format: ^29.5.0 + checksum: 2165cb513094e53e456e0c73d5e8f2726fb9f92768dc954bb82f89acf3f630ed8ba86f272fc01f074bcdf794f99b866f870a3a167afc40b65a9a3f4410ee8ddc + languageName: node + linkType: hard + "@volar/language-core@npm:1.10.1, @volar/language-core@npm:~1.10.0": version: 1.10.1 resolution: "@volar/language-core@npm:1.10.1" @@ -11462,6 +11544,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: 25456b2aa333250f01143968e02e4884a34588a8538fbbf65c91a637f1dbfb8069249133cd2f4e530f10f624d206a664e7df30207830b659e9f5298b00a4099b + languageName: node + linkType: hard + "assign-symbols@npm:^1.0.0": version: 1.0.0 resolution: "assign-symbols@npm:1.0.0" @@ -12890,6 +12979,21 @@ __metadata: languageName: node linkType: hard +"chai@npm:^4.3.7": + version: 4.3.7 + resolution: "chai@npm:4.3.7" + dependencies: + assertion-error: ^1.1.0 + check-error: ^1.0.2 + deep-eql: ^4.1.2 + get-func-name: ^2.0.0 + loupe: ^2.3.1 + pathval: ^1.1.1 + type-detect: ^4.0.5 + checksum: a11c6b74ce2d5587c3db1f1e5bf32073876319d4c65ba4e574ca9b56ec93ebbc80765e1fa4af354553afbf7ed245fb54c45d69d350a7b850c4aaf9f1e01f950f + languageName: node + linkType: hard + "chalk@npm:4.1.0": version: 4.1.0 resolution: "chalk@npm:4.1.0" @@ -13017,6 +13121,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^1.0.2": + version: 1.0.2 + resolution: "check-error@npm:1.0.2" + checksum: c58ac4d6a92203209a61d025568198c073f101691eb6247f999266e1d1e3ab3af2bbe0a41af5008c1f1b95446ec7831e6ba91f03816177f2da852f316ad7921d + languageName: node + linkType: hard + "checkup@npm:^1.3.0": version: 1.3.0 resolution: "checkup@npm:1.3.0" @@ -14513,6 +14624,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^4.1.2": + version: 4.1.3 + resolution: "deep-eql@npm:4.1.3" + dependencies: + type-detect: ^4.0.0 + checksum: ff34e8605d8253e1bf9fe48056e02c6f347b81d9b5df1c6650a1b0f6f847b4a86453b16dc226b34f853ef14b626e85d04e081b022e20b00cd7d54f079ce9bbdd + languageName: node + linkType: hard + "deep-equal@npm:^1.1.1": version: 1.1.1 resolution: "deep-equal@npm:1.1.1" @@ -14807,7 +14927,7 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.6.3": +"diff-sequences@npm:^29.4.3, diff-sequences@npm:^29.6.3": version: 29.6.3 resolution: "diff-sequences@npm:29.6.3" checksum: 32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 @@ -16686,6 +16806,20 @@ __metadata: languageName: node linkType: hard +"expect@npm:^29.6.2": + version: 29.6.2 + resolution: "expect@npm:29.6.2" + dependencies: + "@jest/expect-utils": ^29.6.2 + "@types/node": "*" + jest-get-type: ^29.4.3 + jest-matcher-utils: ^29.6.2 + jest-message-util: ^29.6.2 + jest-util: ^29.6.2 + checksum: c45d700947f23b8d5d034678733a35bdb404c3c1f26163ef692f9a05ae0b85b1918aea030ecf5efb7ffc61f5363593a57247483078187f2406736b5c9e6ceef9 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -17766,6 +17900,13 @@ __metadata: languageName: node linkType: hard +"get-func-name@npm:^2.0.0": + version: 2.0.0 + resolution: "get-func-name@npm:2.0.0" + checksum: ed8791f7ba92cfd747259dff7ec8b6cc42734cebd031fb58c99a6e71d24d3532d84b46ad7806cafad6ad21784dd04ae1808a002d2b21001425e21f5f394c34e7 + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": version: 1.2.1 resolution: "get-intrinsic@npm:1.2.1" @@ -20347,6 +20488,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-diff@npm:29.6.2" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^29.4.3 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.2 + checksum: 341948de9fbbf9884a93e17a6ff638112109ca640420d62a26753329fdf1d6d0b4582a2a2e894cac4120dc8fb251361d58917288e9f1b955d286b78ab1db29c3 + languageName: node + linkType: hard + "jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" @@ -20423,7 +20576,7 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^29.6.3": +"jest-get-type@npm:^29.4.3, jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" checksum: 552e7a97a983d3c2d4e412a44eb7de0430ff773dd99f7500962c268d6dfbfa431d7d08f919c9d960530e5f7f78eb47f267ad9b318265e5092b3ff9ede0db7c2b @@ -20508,6 +20661,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-matcher-utils@npm:29.6.2" + dependencies: + chalk: ^4.0.0 + jest-diff: ^29.6.2 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.2 + checksum: 3b383753b8744431a7003c00749ee1ecb16ec4f2e23c8214a95a77e27687d4b3ddd0f4afec85fa5b4ee950c69452de35dc3f07a2da41ff5914dd04bf2decf79e + languageName: node + linkType: hard + "jest-matcher-utils@npm:^29.7.0": version: 29.7.0 resolution: "jest-matcher-utils@npm:29.7.0" @@ -20520,6 +20685,23 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-message-util@npm:29.6.2" + dependencies: + "@babel/code-frame": ^7.12.13 + "@jest/types": ^29.6.1 + "@types/stack-utils": ^2.0.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + micromatch: ^4.0.4 + pretty-format: ^29.6.2 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: 4c5624ff281aa9ea09a7fca09f58b39282d3e452651d335d68834795e59c131f20d7fa174ebededa3399f1474af410c434c4dfdba57a99e3e8acf265fbb7c123 + languageName: node + linkType: hard + "jest-message-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-message-util@npm:29.7.0" @@ -20757,6 +20939,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-util@npm:29.6.2" + dependencies: + "@jest/types": ^29.6.1 + "@types/node": "*" + chalk: ^4.0.0 + ci-info: ^3.2.0 + graceful-fs: ^4.2.9 + picomatch: ^2.2.3 + checksum: 2510385dc2b828c5de89d1508984ab74edf010377e8a3664549eb1da74f96e8a1badd535b3b8da286c4e2ccfa9de4f07cb501f6c91dc7b5bc5618c248b2b2f0a + languageName: node + linkType: hard + "jest-validate@npm:^29.7.0": version: 29.7.0 resolution: "jest-validate@npm:29.7.0" @@ -22121,6 +22317,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^2.3.1, loupe@npm:^2.3.6": + version: 2.3.6 + resolution: "loupe@npm:2.3.6" + dependencies: + get-func-name: ^2.0.0 + checksum: a974841ce94ef2a35aac7144e7f9e789e3887f82286cd9ffe7ff00f2ac9d117481989948657465e2b0b102f23136d89ae0a18fd4a32d9015012cd64464453289 + languageName: node + linkType: hard + "lower-case@npm:^2.0.2": version: 2.0.2 resolution: "lower-case@npm:2.0.2" @@ -25599,6 +25804,13 @@ __metadata: languageName: node linkType: hard +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: f63e1bc1b33593cdf094ed6ff5c49c1c0dc5dc20a646ca9725cc7fe7cd9995002d51d5685b9b2ec6814342935748b711bafa840f84c0bb04e38ff40a335c94dc + languageName: node + linkType: hard + "pbkdf2@npm:^3.0.3": version: 3.1.2 resolution: "pbkdf2@npm:3.1.2" @@ -26211,6 +26423,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.5.0, pretty-format@npm:^29.6.2": + version: 29.6.2 + resolution: "pretty-format@npm:29.6.2" + dependencies: + "@jest/schemas": ^29.6.0 + ansi-styles: ^5.0.0 + react-is: ^18.0.0 + checksum: fb9b404f61c48d1b9408ec93b9dbd0266c1fed36dc99c2ebaa42c1da2354f201a57862a28455ddb573e9b14e74cb8542928c0ef85955b7fd7e3e70ce42e52b32 + languageName: node + linkType: hard + "pretty-format@npm:^3.8.0": version: 3.8.0 resolution: "pretty-format@npm:3.8.0" @@ -30408,6 +30631,13 @@ __metadata: languageName: node linkType: hard +"tinyspy@npm:^2.1.1": + version: 2.1.1 + resolution: "tinyspy@npm:2.1.1" + checksum: 0e7186fd380cbc594c35a0f6270f61b79ed22d1d960cac6064c3a5ebcf8a3a70d6590ff2049cba1d58631c3c556b1a67896d0db136381da7855a37087a90fbc5 + languageName: node + linkType: hard + "tmp@npm:0.0.28": version: 0.0.28 resolution: "tmp@npm:0.0.28" @@ -30866,7 +31096,7 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:4.0.8": +"type-detect@npm:4.0.8, type-detect@npm:^4.0.0, type-detect@npm:^4.0.5": version: 4.0.8 resolution: "type-detect@npm:4.0.8" checksum: 8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd From f0d68a1971f4d9b0f6705775f74681c6a18cabbc Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 25 Aug 2023 16:11:48 +0200 Subject: [PATCH 02/36] Replace expect (from jest) with @vitest/expect --- .../lib/instrumenter/src/instrumenter.test.ts | 38 +++++ code/lib/instrumenter/src/instrumenter.ts | 37 +++-- code/lib/instrumenter/src/types.ts | 1 + code/lib/test/src/expect.ts | 136 ++++++++++++++++++ code/lib/test/src/index.ts | 46 +++--- 5 files changed, 215 insertions(+), 43 deletions(-) create mode 100644 code/lib/test/src/expect.ts diff --git a/code/lib/instrumenter/src/instrumenter.test.ts b/code/lib/instrumenter/src/instrumenter.test.ts index eadb1f7d5c84..184c6773a747 100644 --- a/code/lib/instrumenter/src/instrumenter.test.ts +++ b/code/lib/instrumenter/src/instrumenter.test.ts @@ -112,6 +112,44 @@ describe('Instrumenter', () => { expect(result.fn1.fn2.__originalFn__).toBe(fn1.fn2); }); + it('patches functions correctly that reference this', () => { + const object = { + name: 'name', + method() { + return this.name; + }, + }; + + const instrumented = instrument(object); + expect(object.method()).toEqual(instrumented.method()); + + expect(instrumented.method).toEqual(expect.any(Function)); + expect(instrumented.method.__originalFn__).toBe(object.method); + }); + + it('patches functions correctly that use proxies', () => { + const object = new Proxy( + { + name: 'name', + method() { + return this.name; + }, + }, + { + get(target, prop, receiver) { + if (prop === 'name') return `${target[prop]}!`; + return Reflect.get(target, prop, receiver); + }, + } + ); + + const instrumented = instrument(object); + expect(object.method()).toEqual(instrumented.method()); + + expect(instrumented.method).toEqual(expect.any(Function)); + expect(instrumented.method.__originalFn__).toBe(object.method); + }); + it('patched functions call the original function when invoked', () => { const { fn } = instrument({ fn: jest.fn() }); const obj = {}; diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 357b9df52817..21f05a0ea585 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -24,8 +24,8 @@ export const EVENTS = { END: 'storybook/instrumenter/end', }; -type PatchedObj = { - [Property in keyof TObj]: TObj[Property] & { __originalFn__: PatchedObj }; +type PatchedObj> = { + [Property in keyof TObj]: TObj[Property] & { __originalFn__: TObj[Property] }; }; const controlsDisabled: ControlStates = { @@ -49,7 +49,6 @@ const isInstrumentable = (o: unknown) => { if (o.constructor === undefined) return true; const proto = o.constructor.prototype; if (!isObject(proto)) return false; - if (Object.prototype.hasOwnProperty.call(proto, 'isPrototypeOf') === false) return false; return true; }; @@ -290,11 +289,13 @@ export class Instrumenter { // Traverses the object structure to recursively patch all function properties. // Returns the original object, or a new object with the same constructor, // depending on whether it should mutate. - instrument(obj: TObj, options: Options): PatchedObj { - if (!isInstrumentable(obj)) return obj; + instrument>(obj: TObj, options: Options): PatchedObj { + if (!isInstrumentable(obj)) return obj as PatchedObj; const { mutate = false, path = [] } = options; - return Object.keys(obj).reduce( + + const keys = options.getKeys ? options.getKeys(obj) : Object.keys(obj); + return keys.reduce( (acc, key) => { const value = (obj as Record)[key]; @@ -305,13 +306,13 @@ export class Instrumenter { } // Already patched, so we pass through unchanged - if (typeof value.__originalFn__ === 'function') { + if ('__originalFn__' in value && typeof value.__originalFn__ === 'function') { acc[key] = value; return acc; } // Patch the function and mark it "patched" by adding a reference to the original function - acc[key] = (...args: any[]) => this.track(key, value, args, options); + acc[key] = (...args: any[]) => this.track(key, value, obj, args, options); acc[key].__originalFn__ = value; // Reuse the original name as the patched function's name @@ -334,7 +335,13 @@ export class Instrumenter { // Monkey patch an object method to record calls. // Returns a function that invokes the original function, records the invocation ("call") and // returns the original result. - track(method: string, fn: Function, args: any[], options: Options) { + track( + method: string, + fn: Function, + object: Record, + args: any[], + options: Options + ) { const storyId: StoryId = args?.[0]?.__storyId__ || global.__STORYBOOK_PREVIEW__?.selectionStore?.selection?.storyId; const { cursor, ancestors } = this.getState(storyId); @@ -344,11 +351,11 @@ export class Instrumenter { const interceptable = typeof intercept === 'function' ? intercept(method, path) : intercept; const call = { id, cursor, storyId, ancestors, path, method, args, interceptable, retain }; const interceptOrInvoke = interceptable && !ancestors.length ? this.intercept : this.invoke; - const result = interceptOrInvoke.call(this, fn, call, options); + const result = interceptOrInvoke.call(this, fn, object, call, options); return this.instrument(result, { ...options, mutate: true, path: [{ __callId__: call.id }] }); } - intercept(fn: Function, call: Call, options: Options) { + intercept(fn: Function, object: Record, call: Call, options: Options) { const { chainedCallIds, isDebugging, playUntil } = this.getState(call.storyId); // For a "jump to step" action, continue playing until we hit a call by that ID. @@ -358,7 +365,7 @@ export class Instrumenter { if (playUntil === call.id) { this.setState(call.storyId, { playUntil: undefined }); } - return this.invoke(fn, call, options); + return this.invoke(fn, object, call, options); } // Instead of invoking the function, defer the function call until we continue playing. @@ -373,11 +380,11 @@ export class Instrumenter { const { [call.id]: _, ...resolvers } = state.resolvers; return { isLocked: true, resolvers }; }); - return this.invoke(fn, call, options); + return this.invoke(fn, object, call, options); }); } - invoke(fn: Function, call: Call, options: Options) { + invoke(fn: Function, object: Record, call: Call, options: Options) { // TODO this doesnt work because the abortSignal we have here is the newly created one // const { abortSignal } = global.window.__STORYBOOK_PREVIEW__ || {}; // if (abortSignal && abortSignal.aborted) throw IGNORED_EXCEPTION; @@ -510,7 +517,7 @@ export class Instrumenter { }; }); - const result = fn(...finalArgs); + const result = fn.apply(object, finalArgs); // Track the result so we can trace later uses of it back to the originating call. // Primitive results (undefined, null, boolean, string, number, BigInt) are ignored. diff --git a/code/lib/instrumenter/src/types.ts b/code/lib/instrumenter/src/types.ts index 1076d4dd3a1d..bfcfc42d1b45 100644 --- a/code/lib/instrumenter/src/types.ts +++ b/code/lib/instrumenter/src/types.ts @@ -90,4 +90,5 @@ export interface Options { mutate?: boolean; path?: Array; getArgs?: (call: Call, state: State) => Call['args']; + getKeys?: (originalObject: Record) => string[]; } diff --git a/code/lib/test/src/expect.ts b/code/lib/test/src/expect.ts new file mode 100644 index 000000000000..d9c63b2bbb8d --- /dev/null +++ b/code/lib/test/src/expect.ts @@ -0,0 +1,136 @@ +import * as chai from 'chai'; +import type { + AsymmetricMatchersContaining, + ExpectStatic, + JestAssertion, + MatchersObject, + MatcherState, +} from '@vitest/expect'; + +import { + getState, + GLOBAL_EXPECT, + JestAsymmetricMatchers, + JestChaiExpect, + JestExtend, + setState, +} from '@vitest/expect'; +import * as matchers from '@testing-library/jest-dom/matchers'; + +type Promisify = { + [K in keyof O]: O[K] extends (...args: infer A) => infer R + ? O extends R + ? Promisify + : (...args: A) => Promise + : O[K]; +}; + +// We only expose the jest compatible API for now +export interface Assertion extends Promisify> { + toHaveBeenCalledOnce(): Promise; + toSatisfy(matcher: (value: E) => boolean, message?: string): Promise; + resolves: Promisify>; + rejects: Promisify>; +} + +export interface Expect extends AsymmetricMatchersContaining { + (actual: T, message?: string): Assertion; + unreachable(message?: string): Promise; + soft(actual: T, message?: string): Assertion; + extend(expects: MatchersObject): void; + assertions(expected: number): Promise; + hasAssertions(): Promise; + anything(): any; + any(constructor: unknown): any; + getState(): MatcherState; + setState(state: Partial): void; + not: AsymmetricMatchersContaining; +} + +export function createExpect() { + chai.use(JestExtend); + chai.use(JestChaiExpect); + chai.use(JestAsymmetricMatchers); + + const expect = ((value: unknown, message?: string) => { + const { assertionCalls } = getState(expect); + setState({ assertionCalls: assertionCalls + 1, soft: false }, expect); + return chai.expect(value, message); + }) as ExpectStatic; + + Object.assign(expect, chai.expect); + + // The below methods are added to make chai jest compatible + + expect.getState = () => getState(expect); + expect.setState = (state) => setState(state as Partial, expect); + + // @ts-expect-error chai.extend is not typed + expect.extend = (expects: MatchersObject) => chai.expect.extend(expect, expects); + + expect.soft = (...args) => { + const assert = expect(...args); + expect.setState({ + soft: true, + }); + return assert; + }; + + expect.unreachable = (message?: string): never => { + chai.assert.fail(`expected${message ? ` "${message}" ` : ' '}not to be reached`); + }; + + function assertions(expected: number) { + const errorGen = () => + new Error( + `expected number of assertions to be ${expected}, but got ${ + expect.getState().assertionCalls + }` + ); + if (Error.captureStackTrace) Error.captureStackTrace(errorGen(), assertions); + + expect.setState({ + expectedAssertionsNumber: expected, + expectedAssertionsNumberErrorGen: errorGen, + }); + } + + function hasAssertions() { + const error = new Error('expected any number of assertion, but got none'); + if (Error.captureStackTrace) Error.captureStackTrace(error, hasAssertions); + + expect.setState({ + isExpectingAssertions: true, + isExpectingAssertionsError: error, + }); + } + + setState( + { + // this should also add "snapshotState" that is added conditionally + assertionCalls: 0, + isExpectingAssertions: false, + isExpectingAssertionsError: null, + expectedAssertionsNumber: null, + expectedAssertionsNumberErrorGen: null, + }, + expect + ); + + chai.util.addMethod(expect, 'assertions', assertions); + chai.util.addMethod(expect, 'hasAssertions', hasAssertions); + expect.extend(matchers); + + return expect as unknown as Expect; +} + +const expect = createExpect(); + +// @vitest/expect expects this to be set +Object.defineProperty(globalThis, GLOBAL_EXPECT, { + value: expect, + writable: true, + configurable: true, +}); + +export { expect }; diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 4d44c09ba333..2371206510bf 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -1,32 +1,22 @@ -/* eslint-disable import/no-extraneous-dependencies,import/no-named-default */ -import { default as expectPatched } from '@storybook/expect'; import { instrument } from '@storybook/instrumenter'; -import * as matchers from '@testing-library/jest-dom/matchers'; -import * as _mock from '@vitest/spy'; -export type * from '@vitest/spy'; +import * as spy from '@vitest/spy'; +import chai from 'chai'; +import { expect as rawExpect } from './expect'; -export const { mock } = instrument({ mock: _mock }, { retain: true }); +export * from '@vitest/spy'; -/** - * The `expect` function is used every time you want to test a value. - * You will rarely call `expect` by itself. - */ -export interface Expect extends Pick { - /** - * The `expect` function is used every time you want to test a value. - * You will rarely call `expect` by itself. - * - * @param actual The value to apply matchers against. - */ - (actual: T): jest.JestMatchersShape< - jest.Matchers, T>, - jest.Matchers, T> - >; -} +export const { fn } = instrument({ fn: spy.fn }, { retain: true }); -expectPatched.extend(matchers); - -export const expect: Expect = instrument( - { expect: expectPatched }, - { intercept: (_method, path) => path[0] !== 'expect' } -).expect as unknown as Expect; +export const { expect } = instrument( + { expect: rawExpect }, + { + getKeys: (obj) => { + const privateApi = ['assert', '__methods', '__flags']; + if (obj.constructor === chai.Assertion) { + return Object.keys(Object.getPrototypeOf(obj)).filter((it) => !privateApi.includes(it)); + } + return Object.keys(obj); + }, + intercept: true, + } +); From 97350aa59e0508974a5290f79b1b65f27e98fb7d Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 25 Aug 2023 16:48:20 +0200 Subject: [PATCH 03/36] Fix build --- code/lib/test/package.json | 1 - code/lib/test/src/index.ts | 2 +- code/yarn.lock | 144 ++++++++++++++++--------------------- 3 files changed, 63 insertions(+), 84 deletions(-) diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 92d9ed1995ea..67cb6a46ea1f 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -52,7 +52,6 @@ }, "devDependencies": { "@types/chai": "^4", - "@types/testing-library__jest-dom": "^6", "type-fest": "~2.19", "typescript": "~4.9.3" }, diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 2371206510bf..cd6dc6167a13 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -10,7 +10,7 @@ export const { fn } = instrument({ fn: spy.fn }, { retain: true }); export const { expect } = instrument( { expect: rawExpect }, { - getKeys: (obj) => { + getKeys: (obj: Record) => { const privateApi = ['assert', '__methods', '__flags']; if (obj.constructor === chai.Assertion) { return Object.keys(Object.getPrototypeOf(obj)).filter((it) => !privateApi.includes(it)); diff --git a/code/yarn.lock b/code/yarn.lock index 284c1a3103d0..12aec53bd4eb 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -3570,12 +3570,12 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/expect-utils@npm:29.6.2" +"@jest/expect-utils@npm:^29.6.4": + version: 29.6.4 + resolution: "@jest/expect-utils@npm:29.6.4" dependencies: - jest-get-type: ^29.4.3 - checksum: 9a8dd5197d7e2d7f25a697dcb26dcf1483c5a9eccd1637ebd95b6deded6dd6d7d5513ceb1295119cd979abfc78d814ed00ff118a752028adb6646231ee234086 + jest-get-type: ^29.6.3 + checksum: 17d87d551090f6b460fa45605c614b2ad28e257360a5b8152216fe983370f4cfb8482d2d017552c2be43be1caa0ff5594f1381be17798dcad3899e05b297fe83 languageName: node linkType: hard @@ -3670,7 +3670,7 @@ __metadata: languageName: node linkType: hard -"@jest/schemas@npm:^29.4.3, @jest/schemas@npm:^29.6.0, @jest/schemas@npm:^29.6.3": +"@jest/schemas@npm:^29.4.3, @jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" dependencies: @@ -3763,7 +3763,7 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^29.6.1, @jest/types@npm:^29.6.3": +"@jest/types@npm:^29.6.3": version: 29.6.3 resolution: "@jest/types@npm:29.6.3" dependencies: @@ -8154,7 +8154,6 @@ __metadata: "@storybook/instrumenter": "workspace:^" "@testing-library/jest-dom": ^6.0.0 "@types/chai": ^4 - "@types/testing-library__jest-dom": ^6 "@vitest/expect": ^0.34.2 "@vitest/spy": ^0.34.1 chai: ^4.3.7 @@ -9815,15 +9814,6 @@ __metadata: languageName: node linkType: hard -"@types/testing-library__jest-dom@npm:^6": - version: 6.0.0 - resolution: "@types/testing-library__jest-dom@npm:6.0.0" - dependencies: - "@testing-library/jest-dom": "*" - checksum: 824950dc82752ddb656fa7ca851bf454804332f7a7a8571231abfc3553902198c6b96de209bb3dd1f1a15ee6a269a1efa56866041f5692ee0129308772e658e0 - languageName: node - linkType: hard - "@types/tmp@npm:^0.2.3": version: 0.2.3 resolution: "@types/tmp@npm:0.2.3" @@ -10180,42 +10170,33 @@ __metadata: linkType: hard "@vitest/expect@npm:^0.34.2": - version: 0.34.2 - resolution: "@vitest/expect@npm:0.34.2" + version: 0.34.3 + resolution: "@vitest/expect@npm:0.34.3" dependencies: - "@vitest/spy": 0.34.2 - "@vitest/utils": 0.34.2 + "@vitest/spy": 0.34.3 + "@vitest/utils": 0.34.3 chai: ^4.3.7 - checksum: 80119614a826996a36f861e7b41cc41771009e1c6d1fcfa5c024d9f5aba83c2731058dc6869fef31e29631c68bec939c8373c4905c0612e860829fca616b76dc + checksum: ec70fba265b5bbd31f9c06c60e504ee0f1394df0871a5bddbe0a6b0a2436705f5e41e183fcdec3dd8864b5432b14d4f3802b896ba625ae0a44b52a9a08ddbd73 languageName: node linkType: hard -"@vitest/spy@npm:0.34.2": - version: 0.34.2 - resolution: "@vitest/spy@npm:0.34.2" +"@vitest/spy@npm:0.34.3, @vitest/spy@npm:^0.34.1": + version: 0.34.3 + resolution: "@vitest/spy@npm:0.34.3" dependencies: tinyspy: ^2.1.1 - checksum: 98fbcea189d917693321cfded1b11d80ea8f1c1f6c6332ccbacb365e68640c286ecce28b563bb0b786e807d57f9cdd62b845ecf92e88bf184b38034ee85e4c69 + checksum: 8052386c56b55ebaca5ffbf5b91bb73c8c4e0e4ca1c53c922ed947703f54363689df5b4000718bbdc7c79c84e3fb4b788c42efdb8bb8592c44123bd717097f33 languageName: node linkType: hard -"@vitest/spy@npm:^0.34.1": - version: 0.34.1 - resolution: "@vitest/spy@npm:0.34.1" - dependencies: - tinyspy: ^2.1.1 - checksum: ba554f34276df580613027785ab8ca02cfd31cb588b0c506341e9a4aa75e369eaaf9d991119aa11b28e4b66819e659a3dbb4afacf46b387daabad1072746358f - languageName: node - linkType: hard - -"@vitest/utils@npm:0.34.2": - version: 0.34.2 - resolution: "@vitest/utils@npm:0.34.2" +"@vitest/utils@npm:0.34.3": + version: 0.34.3 + resolution: "@vitest/utils@npm:0.34.3" dependencies: diff-sequences: ^29.4.3 loupe: ^2.3.6 pretty-format: ^29.5.0 - checksum: 2165cb513094e53e456e0c73d5e8f2726fb9f92768dc954bb82f89acf3f630ed8ba86f272fc01f074bcdf794f99b866f870a3a167afc40b65a9a3f4410ee8ddc + checksum: 7ca2d82c4286b963b8d8004812464c81b42d73d027b739fa5b2f2cdfb2a14e409f8ad1da6c269ba7379c74a93bb0e81485b2040a18a06a886b49c4bef1e494ab languageName: node linkType: hard @@ -12980,8 +12961,8 @@ __metadata: linkType: hard "chai@npm:^4.3.7": - version: 4.3.7 - resolution: "chai@npm:4.3.7" + version: 4.3.8 + resolution: "chai@npm:4.3.8" dependencies: assertion-error: ^1.1.0 check-error: ^1.0.2 @@ -12990,7 +12971,7 @@ __metadata: loupe: ^2.3.1 pathval: ^1.1.1 type-detect: ^4.0.5 - checksum: a11c6b74ce2d5587c3db1f1e5bf32073876319d4c65ba4e574ca9b56ec93ebbc80765e1fa4af354553afbf7ed245fb54c45d69d350a7b850c4aaf9f1e01f950f + checksum: 5aa714fbbd4b3a1506f4fc9094851bf9109f184d601c1278bcd4eb98d5ee05cc75d7e84f46d072d656502c55544b38c748a1c669468d138e41e5c9d175beffc5 languageName: node linkType: hard @@ -16807,16 +16788,15 @@ __metadata: linkType: hard "expect@npm:^29.6.2": - version: 29.6.2 - resolution: "expect@npm:29.6.2" + version: 29.6.4 + resolution: "expect@npm:29.6.4" dependencies: - "@jest/expect-utils": ^29.6.2 - "@types/node": "*" - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 - checksum: c45d700947f23b8d5d034678733a35bdb404c3c1f26163ef692f9a05ae0b85b1918aea030ecf5efb7ffc61f5363593a57247483078187f2406736b5c9e6ceef9 + "@jest/expect-utils": ^29.6.4 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.6.4 + jest-message-util: ^29.6.3 + jest-util: ^29.6.3 + checksum: d3f4ed2fcc33f743b1dd9cf25a07c2f56c9ddd7e1b327d3e74b5febfc90880a9e2ab10c56b3bf31e14d5ead69dc4cb68f718b7fbc3fae8571f8e18675ffe8080 languageName: node linkType: hard @@ -20488,15 +20468,15 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-diff@npm:29.6.2" +"jest-diff@npm:^29.6.4": + version: 29.6.4 + resolution: "jest-diff@npm:29.6.4" dependencies: chalk: ^4.0.0 - diff-sequences: ^29.4.3 - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: 341948de9fbbf9884a93e17a6ff638112109ca640420d62a26753329fdf1d6d0b4582a2a2e894cac4120dc8fb251361d58917288e9f1b955d286b78ab1db29c3 + diff-sequences: ^29.6.3 + jest-get-type: ^29.6.3 + pretty-format: ^29.6.3 + checksum: 5f96be0f15ba8e70acfa5512ca49ba67363678e7ce222889612385a8d9dd042822fdd22a514394fe726b1f462e605bc5d7fc130bd81fa2247e7d40413975d576 languageName: node linkType: hard @@ -20576,7 +20556,7 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^29.4.3, jest-get-type@npm:^29.6.3": +"jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" checksum: 552e7a97a983d3c2d4e412a44eb7de0430ff773dd99f7500962c268d6dfbfa431d7d08f919c9d960530e5f7f78eb47f267ad9b318265e5092b3ff9ede0db7c2b @@ -20661,15 +20641,15 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-matcher-utils@npm:29.6.2" +"jest-matcher-utils@npm:^29.6.4": + version: 29.6.4 + resolution: "jest-matcher-utils@npm:29.6.4" dependencies: chalk: ^4.0.0 - jest-diff: ^29.6.2 - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: 3b383753b8744431a7003c00749ee1ecb16ec4f2e23c8214a95a77e27687d4b3ddd0f4afec85fa5b4ee950c69452de35dc3f07a2da41ff5914dd04bf2decf79e + jest-diff: ^29.6.4 + jest-get-type: ^29.6.3 + pretty-format: ^29.6.3 + checksum: aa54f7075438160bd29e8c0a02d6b7e6ed1f18bab5670d161d1555e5cfa9b61e86306a260ca0304680fb1b357a944fd1d007b6519f91fc6f67d72997b1a7fdb8 languageName: node linkType: hard @@ -20685,20 +20665,20 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-message-util@npm:29.6.2" +"jest-message-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-message-util@npm:29.6.3" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.6.2 + pretty-format: ^29.6.3 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 4c5624ff281aa9ea09a7fca09f58b39282d3e452651d335d68834795e59c131f20d7fa174ebededa3399f1474af410c434c4dfdba57a99e3e8acf265fbb7c123 + checksum: 5ae17c0aa8076bd0d4c68a036865cf156084cf7b4f69b4ffee0f49da61f7fe9eb38c6405c1f6967df031ffe14f8a31830baa1f04f1dbea52f239689cd4e5b326 languageName: node linkType: hard @@ -20939,17 +20919,17 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-util@npm:29.6.2" +"jest-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-util@npm:29.6.3" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: 2510385dc2b828c5de89d1508984ab74edf010377e8a3664549eb1da74f96e8a1badd535b3b8da286c4e2ccfa9de4f07cb501f6c91dc7b5bc5618c248b2b2f0a + checksum: 9428c07696f27aa8f230a13a35546559f9a087f3e3744f53f69a620598234c03004b808b1b4a12120cc5771a88403bf0a1e3f95a7ccd610acf03d90c36135e88 languageName: node linkType: hard @@ -26423,14 +26403,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.5.0, pretty-format@npm:^29.6.2": - version: 29.6.2 - resolution: "pretty-format@npm:29.6.2" +"pretty-format@npm:^29.5.0, pretty-format@npm:^29.6.3": + version: 29.6.3 + resolution: "pretty-format@npm:29.6.3" dependencies: - "@jest/schemas": ^29.6.0 + "@jest/schemas": ^29.6.3 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: fb9b404f61c48d1b9408ec93b9dbd0266c1fed36dc99c2ebaa42c1da2354f201a57862a28455ddb573e9b14e74cb8542928c0ef85955b7fd7e3e70ce42e52b32 + checksum: 73c6a46acdad4cb9337add02c850769fb831d7154cdb50b1152f3970a8fbf8292188dcccd1ba597f3e34c360af71fc0b63f1db4cf155a0098ffe2812eb7a6b22 languageName: node linkType: hard From daeffb5e5930ac770e1f06f2fbbff6901e1c8565 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 19 Sep 2023 09:20:15 +0200 Subject: [PATCH 04/36] Move testing library to test package + some improvements --- code/lib/test/package.json | 8 +- code/lib/test/src/expect.ts | 17 ++-- code/lib/test/src/index.ts | 2 + code/lib/test/src/testing-library.ts | 116 +++++++++++++++++++++++++++ code/lib/test/src/utils.ts | 5 ++ code/package.json | 1 - code/yarn.lock | 24 ++---- 7 files changed, 142 insertions(+), 31 deletions(-) create mode 100644 code/lib/test/src/testing-library.ts create mode 100644 code/lib/test/src/utils.ts diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 67cb6a46ea1f..c028e1663218 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -43,12 +43,16 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { + "@storybook/client-logger": "workspace:^", "@storybook/instrumenter": "workspace:^", - "@testing-library/jest-dom": "^6.0.0", + "@testing-library/dom": "^9.3.1", + "@testing-library/jest-dom": "^6.1.3", + "@testing-library/user-event": "^14.4.3", "@vitest/expect": "^0.34.2", "@vitest/spy": "^0.34.1", "chai": "^4.3.7", - "expect": "^29.6.2" + "expect": "^29.6.2", + "ts-dedent": "^2.2.0" }, "devDependencies": { "@types/chai": "^4", diff --git a/code/lib/test/src/expect.ts b/code/lib/test/src/expect.ts index d9c63b2bbb8d..04d4a05dc366 100644 --- a/code/lib/test/src/expect.ts +++ b/code/lib/test/src/expect.ts @@ -6,7 +6,6 @@ import type { MatchersObject, MatcherState, } from '@vitest/expect'; - import { getState, GLOBAL_EXPECT, @@ -16,21 +15,15 @@ import { setState, } from '@vitest/expect'; import * as matchers from '@testing-library/jest-dom/matchers'; - -type Promisify = { - [K in keyof O]: O[K] extends (...args: infer A) => infer R - ? O extends R - ? Promisify - : (...args: A) => Promise - : O[K]; -}; +import type { PromisifyObject } from './utils'; // We only expose the jest compatible API for now -export interface Assertion extends Promisify> { +export interface Assertion extends PromisifyObject> { toHaveBeenCalledOnce(): Promise; toSatisfy(matcher: (value: E) => boolean, message?: string): Promise; - resolves: Promisify>; - rejects: Promisify>; + resolves: Assertion; + rejects: Assertion; + not: Assertion; } export interface Expect extends AsymmetricMatchersContaining { diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index cd6dc6167a13..2a80a0401e43 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -20,3 +20,5 @@ export const { expect } = instrument( intercept: true, } ); + +export * from './testing-library'; diff --git a/code/lib/test/src/testing-library.ts b/code/lib/test/src/testing-library.ts new file mode 100644 index 000000000000..ade77cdccfc2 --- /dev/null +++ b/code/lib/test/src/testing-library.ts @@ -0,0 +1,116 @@ +/* eslint-disable prefer-destructuring,@typescript-eslint/ban-types */ +import { once } from '@storybook/client-logger'; +import { instrument } from '@storybook/instrumenter'; +import * as domTestingLibrary from '@testing-library/dom'; +import _userEvent from '@testing-library/user-event'; +import dedent from 'ts-dedent'; +import type { FireFunction, FireObject } from '@testing-library/dom/types/events'; +import type { BoundFunctions } from '@testing-library/dom'; +import type { Promisify, PromisifyObject } from './utils'; + +type TestingLibraryDom = typeof domTestingLibrary; + +const testingLibrary = instrument( + { ...domTestingLibrary }, + { + intercept: (method, path) => + path[0] === 'fireEvent' || + method.startsWith('find') || + method.startsWith('waitFor') || + method.startsWith('query') || + method.startsWith('get'), + } +) as {} as PromisifyObject> & { + fireEvent: Promisify & PromisifyObject; + within: ( + element: HTMLElement + ) => PromisifyObject>; +}; + +testingLibrary.screen = new Proxy(testingLibrary.screen, { + get(target, prop, receiver) { + once.warn(dedent` + You are using Testing Library's \`screen\` object. Use \`within(canvasElement)\` instead. + More info: https://storybook.js.org/docs/react/essentials/interactions + `); + return Reflect.get(target, prop, receiver); + }, +}); + +export const { + buildQueries, + configure, + createEvent, + fireEvent, + findAllByAltText, + findAllByDisplayValue, + findAllByLabelText, + findAllByPlaceholderText, + findAllByRole, + findAllByTestId, + findAllByText, + findAllByTitle, + findByAltText, + findByDisplayValue, + findByLabelText, + findByPlaceholderText, + findByRole, + findByTestId, + findByText, + findByTitle, + getAllByAltText, + getAllByDisplayValue, + getAllByLabelText, + getAllByPlaceholderText, + getAllByRole, + getAllByTestId, + getAllByText, + getAllByTitle, + getByAltText, + getByDisplayValue, + getByLabelText, + getByPlaceholderText, + getByRole, + getByTestId, + getByText, + getByTitle, + getConfig, + getDefaultNormalizer, + getElementError, + getNodeText, + getQueriesForElement, + getRoles, + getSuggestedQuery, + isInaccessible, + logDOM, + logRoles, + prettyDOM, + queries, + queryAllByAltText, + queryAllByAttribute, + queryAllByDisplayValue, + queryAllByLabelText, + queryAllByPlaceholderText, + queryAllByRole, + queryAllByTestId, + queryAllByText, + queryAllByTitle, + queryByAltText, + queryByAttribute, + queryByDisplayValue, + queryByLabelText, + queryByPlaceholderText, + queryByRole, + queryByTestId, + queryByText, + queryByTitle, + queryHelpers, + waitFor, + waitForElementToBeRemoved, + within, + prettyFormat, +} = testingLibrary; + +export const screen = testingLibrary.screen; + +export const { userEvent } = instrument({ userEvent: _userEvent }, { intercept: true }); diff --git a/code/lib/test/src/utils.ts b/code/lib/test/src/utils.ts new file mode 100644 index 000000000000..53029f3c5a00 --- /dev/null +++ b/code/lib/test/src/utils.ts @@ -0,0 +1,5 @@ +export type Promisify = Fn extends (...args: infer A) => infer R + ? (...args: A) => R extends Promise ? R : Promise + : Fn; + +export type PromisifyObject = { -readonly [K in keyof O]: Promisify }; diff --git a/code/package.json b/code/package.json index 386254893d93..825d56674a22 100644 --- a/code/package.json +++ b/code/package.json @@ -201,7 +201,6 @@ "@testing-library/dom": "^7.29.4", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.2", - "@testing-library/user-event": "^13.2.1", "@types/express": "^4.17.11", "@types/fs-extra": "^11.0.1", "@types/lodash": "^4.14.167", diff --git a/code/yarn.lock b/code/yarn.lock index 12aec53bd4eb..1f085381e8bf 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6739,7 +6739,7 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@workspace:*, @storybook/client-logger@workspace:lib/client-logger": +"@storybook/client-logger@workspace:*, @storybook/client-logger@workspace:^, @storybook/client-logger@workspace:lib/client-logger": version: 0.0.0-use.local resolution: "@storybook/client-logger@workspace:lib/client-logger" dependencies: @@ -7892,7 +7892,6 @@ __metadata: "@testing-library/dom": ^7.29.4 "@testing-library/jest-dom": ^5.11.9 "@testing-library/react": ^11.2.2 - "@testing-library/user-event": ^13.2.1 "@types/express": ^4.17.11 "@types/fs-extra": ^11.0.1 "@types/lodash": ^4.14.167 @@ -8151,13 +8150,17 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/test@workspace:lib/test" dependencies: + "@storybook/client-logger": "workspace:^" "@storybook/instrumenter": "workspace:^" - "@testing-library/jest-dom": ^6.0.0 + "@testing-library/dom": ^9.3.1 + "@testing-library/jest-dom": ^6.1.3 + "@testing-library/user-event": ^14.4.3 "@types/chai": ^4 "@vitest/expect": ^0.34.2 "@vitest/spy": ^0.34.1 chai: ^4.3.7 expect: ^29.6.2 + ts-dedent: ^2.2.0 type-fest: ~2.19 typescript: ~4.9.3 languageName: unknown @@ -8750,7 +8753,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:^9.0.0": +"@testing-library/dom@npm:^9.0.0, @testing-library/dom@npm:^9.3.1": version: 9.3.1 resolution: "@testing-library/dom@npm:9.3.1" dependencies: @@ -8796,18 +8799,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/user-event@npm:^13.2.1": - version: 13.5.0 - resolution: "@testing-library/user-event@npm:13.5.0" - dependencies: - "@babel/runtime": ^7.12.5 - peerDependencies: - "@testing-library/dom": ">=7.21.4" - checksum: ff57edaeab31322c80c3f01d55404b4cebb907b9ec7672b96a1a14d053f172046b01c5f27b45677927ebee8ed91bce695a7d09edec9a48875cfacabe39d0426a - languageName: node - linkType: hard - -"@testing-library/user-event@npm:^14.0.0": +"@testing-library/user-event@npm:^14.0.0, @testing-library/user-event@npm:^14.4.3": version: 14.4.3 resolution: "@testing-library/user-event@npm:14.4.3" peerDependencies: From 1f02904a0999d8a949b8851b8862a2962d342290 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 19 Sep 2023 11:28:24 +0200 Subject: [PATCH 05/36] Add testing library assertion on expect --- code/lib/test/src/expect.ts | 11 ++++++++--- code/package.json | 1 - code/yarn.lock | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/code/lib/test/src/expect.ts b/code/lib/test/src/expect.ts index 04d4a05dc366..d277380a3596 100644 --- a/code/lib/test/src/expect.ts +++ b/code/lib/test/src/expect.ts @@ -15,10 +15,13 @@ import { setState, } from '@vitest/expect'; import * as matchers from '@testing-library/jest-dom/matchers'; +import type { TestingLibraryMatchers } from '@testing-library/jest-dom/types/matchers'; import type { PromisifyObject } from './utils'; // We only expose the jest compatible API for now -export interface Assertion extends PromisifyObject> { +export interface Assertion + extends PromisifyObject>, + TestingLibraryMatchers, Promise> { toHaveBeenCalledOnce(): Promise; toSatisfy(matcher: (value: E) => boolean, message?: string): Promise; resolves: Assertion; @@ -80,7 +83,8 @@ export function createExpect() { expect.getState().assertionCalls }` ); - if (Error.captureStackTrace) Error.captureStackTrace(errorGen(), assertions); + if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') + Error.captureStackTrace(errorGen(), assertions); expect.setState({ expectedAssertionsNumber: expected, @@ -90,7 +94,8 @@ export function createExpect() { function hasAssertions() { const error = new Error('expected any number of assertion, but got none'); - if (Error.captureStackTrace) Error.captureStackTrace(error, hasAssertions); + if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') + Error.captureStackTrace(error, hasAssertions); expect.setState({ isExpectingAssertions: true, diff --git a/code/package.json b/code/package.json index 825d56674a22..910f34d1dace 100644 --- a/code/package.json +++ b/code/package.json @@ -80,7 +80,6 @@ ], "resolutions": { "@playwright/test": "1.36.0", - "@testing-library/jest-dom": "^5.11.9", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/experimental-utils": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", diff --git a/code/yarn.lock b/code/yarn.lock index 1f085381e8bf..582d4f569d5b 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -24,7 +24,7 @@ __metadata: languageName: node linkType: hard -"@adobe/css-tools@npm:^4.0.1": +"@adobe/css-tools@npm:^4.0.1, @adobe/css-tools@npm:^4.3.0": version: 4.3.1 resolution: "@adobe/css-tools@npm:4.3.1" checksum: 05672719b544cc0c21ae3ed0eb6349bf458e9d09457578eeeb07cf0f696469ac6417e9c9be1b129e5d6a18098a061c1db55b2275591760ef30a79822436fcbfa @@ -8769,7 +8769,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/jest-dom@npm:^5.11.9": +"@testing-library/jest-dom@npm:^5.11.9, @testing-library/jest-dom@npm:^5.16.2": version: 5.17.0 resolution: "@testing-library/jest-dom@npm:5.17.0" dependencies: @@ -8786,6 +8786,36 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:^6.1.3": + version: 6.1.3 + resolution: "@testing-library/jest-dom@npm:6.1.3" + dependencies: + "@adobe/css-tools": ^4.3.0 + "@babel/runtime": ^7.9.2 + aria-query: ^5.0.0 + chalk: ^3.0.0 + css.escape: ^1.5.1 + dom-accessibility-api: ^0.5.6 + lodash: ^4.17.15 + redent: ^3.0.0 + peerDependencies: + "@jest/globals": ">= 28" + "@types/jest": ">= 28" + jest: ">= 28" + vitest: ">= 0.32" + peerDependenciesMeta: + "@jest/globals": + optional: true + "@types/jest": + optional: true + jest: + optional: true + vitest: + optional: true + checksum: 544e01939d3c14a3d44ae2e2bb9fe2a0cb5a9e4992ca2728f41188fb9fb2d56e25f1a2e1c12000be2a94d8da36cb220b24020e1b5c5c4c4bede9058a0d80583d + languageName: node + linkType: hard + "@testing-library/react@npm:^11.2.2": version: 11.2.7 resolution: "@testing-library/react@npm:11.2.7" From b406b6df85d3aa689c99f255393c19b343061384 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 20 Sep 2023 14:09:29 +0200 Subject: [PATCH 06/36] Make sure synchronous methods are always promises, even in the first run --- code/lib/instrumenter/src/instrumenter.ts | 6 +++++- code/lib/test/src/index.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 21f05a0ea585..5159050a8a65 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -351,7 +351,11 @@ export class Instrumenter { const interceptable = typeof intercept === 'function' ? intercept(method, path) : intercept; const call = { id, cursor, storyId, ancestors, path, method, args, interceptable, retain }; const interceptOrInvoke = interceptable && !ancestors.length ? this.intercept : this.invoke; - const result = interceptOrInvoke.call(this, fn, object, call, options); + const promisifyFn = function (this: unknown, ...args: unknown[]) { + const value = fn.apply(this, args); + return interceptable && typeof value?.then !== 'function' ? Promise.resolve(value) : value; + }; + const result = interceptOrInvoke.call(this, promisifyFn, object, call, options); return this.instrument(result, { ...options, mutate: true, path: [{ __callId__: call.id }] }); } diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 2a80a0401e43..548af7f43d2a 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -17,7 +17,7 @@ export const { expect } = instrument( } return Object.keys(obj); }, - intercept: true, + intercept: (method) => method !== 'expect', } ); From 76c109a4fb652a816348cac313f393cca719b549 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 20 Sep 2023 15:02:35 +0200 Subject: [PATCH 07/36] Revert mistake --- code/package.json | 1 + code/yarn.lock | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/code/package.json b/code/package.json index 910f34d1dace..c1ac7e210b62 100644 --- a/code/package.json +++ b/code/package.json @@ -200,6 +200,7 @@ "@testing-library/dom": "^7.29.4", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.2", + "@testing-library/user-event": "^13.2.1", "@types/express": "^4.17.11", "@types/fs-extra": "^11.0.1", "@types/lodash": "^4.14.167", diff --git a/code/yarn.lock b/code/yarn.lock index 582d4f569d5b..46dc57b94071 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -7892,6 +7892,7 @@ __metadata: "@testing-library/dom": ^7.29.4 "@testing-library/jest-dom": ^5.11.9 "@testing-library/react": ^11.2.2 + "@testing-library/user-event": ^13.2.1 "@types/express": ^4.17.11 "@types/fs-extra": ^11.0.1 "@types/lodash": ^4.14.167 @@ -8829,6 +8830,17 @@ __metadata: languageName: node linkType: hard +"@testing-library/user-event@npm:^13.2.1": + version: 13.5.0 + resolution: "@testing-library/user-event@npm:13.5.0" + dependencies: + "@babel/runtime": ^7.12.5 + peerDependencies: + "@testing-library/dom": ">=7.21.4" + checksum: ff57edaeab31322c80c3f01d55404b4cebb907b9ec7672b96a1a14d053f172046b01c5f27b45677927ebee8ed91bce695a7d09edec9a48875cfacabe39d0426a + languageName: node + linkType: hard + "@testing-library/user-event@npm:^14.0.0, @testing-library/user-event@npm:^14.4.3": version: 14.4.3 resolution: "@testing-library/user-event@npm:14.4.3" From d034a444559c74b5f3c5838c85ae1956a900ba75 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 20 Sep 2023 15:46:53 +0200 Subject: [PATCH 08/36] Clear spies and instrument spies by event handler name --- code/addons/interactions/src/preview.ts | 21 ++++++++++++++++++++- code/lib/test/src/index.ts | 10 ++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts index 972126421393..c76e6a3c7a57 100644 --- a/code/addons/interactions/src/preview.ts +++ b/code/addons/interactions/src/preview.ts @@ -12,6 +12,7 @@ import type { } from '@storybook/types'; import { instrument } from '@storybook/instrumenter'; import { ModuleMocker } from 'jest-mock'; +import { Args } from '@storybook/types'; const JestMock = new ModuleMocker(global); const fn = JestMock.fn.bind(JestMock); @@ -56,7 +57,25 @@ const addSpies = (id: string, val: any, key?: string): any => { const addActionsFromArgTypes: ArgsEnhancer = ({ id, initialArgs }) => addSpies(id, initialArgs); -export const argsEnhancers = [addActionsFromArgTypes]; +const instrumentSpies: ArgsEnhancer = ({ initialArgs }) => { + const argTypesWithAction = Object.entries(initialArgs).filter( + ([, value]) => + typeof value === 'function' && + '_isMockFunction' in value && + value._isMockFunction && + !value._instrumented + ); + + return argTypesWithAction.reduce((acc, [key, value]) => { + const instrumented = instrument({ [key]: () => value }, { retain: true })[key]; + acc[key] = instrumented(); + // this enhancer is being called multiple times + value._instrumented = true; + return acc; + }, {} as Args); +}; + +export const argsEnhancers = [addActionsFromArgTypes, instrumentSpies]; export const { step: runStep } = instrument( { diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 548af7f43d2a..7be0af7535f0 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -2,10 +2,16 @@ import { instrument } from '@storybook/instrumenter'; import * as spy from '@vitest/spy'; import chai from 'chai'; import { expect as rawExpect } from './expect'; - +import { FORCE_REMOUNT, STORY_RENDER_PHASE_CHANGED } from '@storybook/core-events'; +import { addons } from '@storybook/preview-api'; export * from '@vitest/spy'; -export const { fn } = instrument({ fn: spy.fn }, { retain: true }); +const channel = addons.getChannel(); + +channel.on(FORCE_REMOUNT, () => spy.spies.forEach((mock) => mock.mockClear())); +channel.on(STORY_RENDER_PHASE_CHANGED, ({ newPhase }) => { + if (newPhase === 'loading') spy.spies.forEach((mock) => mock.mockClear()); +}); export const { expect } = instrument( { expect: rawExpect }, From e6b0f0a19e341f5f3a79dbccd15736736efbd9bc Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 20 Sep 2023 16:56:25 +0200 Subject: [PATCH 09/36] Show deprecation message --- code/addons/actions/src/addArgsHelpers.ts | 2 +- .../actions/src/models/ActionOptions.ts | 1 + code/addons/actions/src/runtime/action.ts | 20 +++++++++++++++++++ code/addons/interactions/src/preview.ts | 5 +++-- code/lib/instrumenter/src/instrumenter.ts | 5 +++-- code/lib/test/package.json | 2 ++ code/lib/test/src/index.ts | 3 ++- 7 files changed, 32 insertions(+), 6 deletions(-) diff --git a/code/addons/actions/src/addArgsHelpers.ts b/code/addons/actions/src/addArgsHelpers.ts index fd93bea6e6f0..0dcb56c32f55 100644 --- a/code/addons/actions/src/addArgsHelpers.ts +++ b/code/addons/actions/src/addArgsHelpers.ts @@ -32,7 +32,7 @@ export const inferActionsFromArgTypesRegex: ArgsEnhancer = (context) = return argTypesMatchingRegex.reduce((acc, [name, argType]) => { if (isInInitialArgs(name, initialArgs)) { - acc[name] = action(name); + acc[name] = action(name, { implicit: true }); } return acc; }, {} as Args); diff --git a/code/addons/actions/src/models/ActionOptions.ts b/code/addons/actions/src/models/ActionOptions.ts index 6678e5138929..b503df069d5c 100644 --- a/code/addons/actions/src/models/ActionOptions.ts +++ b/code/addons/actions/src/models/ActionOptions.ts @@ -4,6 +4,7 @@ interface Options { depth: number; // backards compatibility, remove in 7.0 clearOnStoryChange: boolean; limit: number; + implicit: boolean; } export type ActionOptions = Partial & Partial; diff --git a/code/addons/actions/src/runtime/action.ts b/code/addons/actions/src/runtime/action.ts index b17647949b86..889e39405848 100644 --- a/code/addons/actions/src/runtime/action.ts +++ b/code/addons/actions/src/runtime/action.ts @@ -1,5 +1,9 @@ +/* eslint-disable no-underscore-dangle */ import { v4 as uuidv4 } from 'uuid'; +import type { PreviewWeb } from '@storybook/preview-api'; import { addons } from '@storybook/preview-api'; +import type { Renderer } from '@storybook/types'; +import { global } from '@storybook/global'; import { EVENT_ID } from '../constants'; import type { ActionDisplay, ActionOptions, HandlerFunction } from '../models'; import { config } from './configureActions'; @@ -46,6 +50,22 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti }; const handler = function actionHandler(...args: any[]) { + if (options.implicit) { + const preview = + '__STORYBOOK_PREVIEW__' in global + ? (global.__STORYBOOK_PREVIEW__ as PreviewWeb) + : undefined; + if ( + preview?.storyRenders.some( + (render) => render.phase === 'playing' || render.phase === 'rendering' + ) + ) { + console.warn( + 'Can not use implicit actions during rendering or playing of a story. \nSee: [docs page]' + ); + } + } + const channel = addons.getChannel(); const id = uuidv4(); const minDepth = 5; // anything less is really just storybook internals diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts index c76e6a3c7a57..d393a0aefb20 100644 --- a/code/addons/interactions/src/preview.ts +++ b/code/addons/interactions/src/preview.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign,no-underscore-dangle */ /// import { addons } from '@storybook/preview-api'; @@ -9,10 +10,10 @@ import type { PlayFunction, PlayFunctionContext, StepLabel, + Args, } from '@storybook/types'; import { instrument } from '@storybook/instrumenter'; import { ModuleMocker } from 'jest-mock'; -import { Args } from '@storybook/types'; const JestMock = new ModuleMocker(global); const fn = JestMock.fn.bind(JestMock); @@ -34,7 +35,7 @@ const addSpies = (id: string, val: any, key?: string): any => { try { if (Object.prototype.toString.call(val) === '[object Object]') { // We have to mutate the original object for this to survive HMR. - // eslint-disable-next-line no-restricted-syntax, no-param-reassign + // eslint-disable-next-line no-restricted-syntax for (const [k, v] of Object.entries(val)) val[k] = addSpies(id, v, k); return val; } diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 5159050a8a65..9402baf56117 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -351,8 +351,9 @@ export class Instrumenter { const interceptable = typeof intercept === 'function' ? intercept(method, path) : intercept; const call = { id, cursor, storyId, ancestors, path, method, args, interceptable, retain }; const interceptOrInvoke = interceptable && !ancestors.length ? this.intercept : this.invoke; - const promisifyFn = function (this: unknown, ...args: unknown[]) { - const value = fn.apply(this, args); + // eslint-disable-next-line func-names + const promisifyFn = function (this: unknown, ...argz: unknown[]) { + const value = fn.apply(this, argz); return interceptable && typeof value?.then !== 'function' ? Promise.resolve(value) : value; }; const result = interceptOrInvoke.call(this, promisifyFn, object, call, options); diff --git a/code/lib/test/package.json b/code/lib/test/package.json index c028e1663218..38deca4b37bd 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -44,6 +44,8 @@ }, "dependencies": { "@storybook/client-logger": "workspace:^", + "@storybook/core-events": "workspace:^", + "@storybook/preview-api": "workspace:^", "@storybook/instrumenter": "workspace:^", "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^6.1.3", diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 7be0af7535f0..cc3cbdf85367 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -1,9 +1,10 @@ import { instrument } from '@storybook/instrumenter'; import * as spy from '@vitest/spy'; import chai from 'chai'; -import { expect as rawExpect } from './expect'; import { FORCE_REMOUNT, STORY_RENDER_PHASE_CHANGED } from '@storybook/core-events'; import { addons } from '@storybook/preview-api'; +import { expect as rawExpect } from './expect'; + export * from '@vitest/spy'; const channel = addons.getChannel(); From 9d880277a8cd23740d0e77443e3b5ebdb9b8e9a3 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 21 Sep 2023 16:49:59 +0200 Subject: [PATCH 10/36] Revert making get* method's promises and make sure we instrument `not` --- code/lib/instrumenter/src/instrumenter.ts | 44 +++++++++++++++++------ code/lib/instrumenter/src/types.ts | 2 +- code/lib/test/src/index.ts | 7 ++-- code/lib/test/src/testing-library.ts | 15 ++------ 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 9402baf56117..93fce9c957cd 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-underscore-dangle */ +/* eslint-disable no-underscore-dangle,no-param-reassign */ import type { Channel } from '@storybook/channels'; import { addons } from '@storybook/preview-api'; import type { StoryId } from '@storybook/types'; @@ -289,19 +289,34 @@ export class Instrumenter { // Traverses the object structure to recursively patch all function properties. // Returns the original object, or a new object with the same constructor, // depending on whether it should mutate. - instrument>(obj: TObj, options: Options): PatchedObj { + instrument>( + obj: TObj, + options: Options, + depth = 0 + ): PatchedObj { if (!isInstrumentable(obj)) return obj as PatchedObj; const { mutate = false, path = [] } = options; - const keys = options.getKeys ? options.getKeys(obj) : Object.keys(obj); + const keys = options.getKeys ? options.getKeys(obj, depth) : Object.keys(obj); + depth += 1; return keys.reduce( (acc, key) => { const value = (obj as Record)[key]; // Nothing to patch, but might be instrumentable, so we recurse if (typeof value !== 'function') { - acc[key] = this.instrument(value, { ...options, path: path.concat(key) }); + const instrumented = this.instrument( + value, + { ...options, path: path.concat(key) }, + depth + ); + const descriptor = getPropertyDescriptor(obj, key); + if (typeof descriptor?.get === 'function') { + Object.defineProperty(acc, key, { get: () => instrumented }); + } else { + acc[key] = instrumented; + } return acc; } @@ -322,7 +337,7 @@ export class Instrumenter { if (Object.keys(value).length > 0) { Object.assign( acc[key], - this.instrument({ ...value }, { ...options, path: path.concat(key) }) + this.instrument({ ...value }, { ...options, path: path.concat(key) }, depth) ); } @@ -351,12 +366,7 @@ export class Instrumenter { const interceptable = typeof intercept === 'function' ? intercept(method, path) : intercept; const call = { id, cursor, storyId, ancestors, path, method, args, interceptable, retain }; const interceptOrInvoke = interceptable && !ancestors.length ? this.intercept : this.invoke; - // eslint-disable-next-line func-names - const promisifyFn = function (this: unknown, ...argz: unknown[]) { - const value = fn.apply(this, argz); - return interceptable && typeof value?.then !== 'function' ? Promise.resolve(value) : value; - }; - const result = interceptOrInvoke.call(this, promisifyFn, object, call, options); + const result = interceptOrInvoke.call(this, fn, object, call, options); return this.instrument(result, { ...options, mutate: true, path: [{ __callId__: call.id }] }); } @@ -649,3 +659,15 @@ export function instrument>( return obj; } } + +function getPropertyDescriptor(obj: T, propName: keyof T) { + let target = obj; + while (target != null) { + const descriptor = Object.getOwnPropertyDescriptor(target, propName); + if (descriptor) { + return descriptor; + } + target = Object.getPrototypeOf(target); + } + return undefined; +} diff --git a/code/lib/instrumenter/src/types.ts b/code/lib/instrumenter/src/types.ts index bfcfc42d1b45..0a528a5c161a 100644 --- a/code/lib/instrumenter/src/types.ts +++ b/code/lib/instrumenter/src/types.ts @@ -90,5 +90,5 @@ export interface Options { mutate?: boolean; path?: Array; getArgs?: (call: Call, state: State) => Call['args']; - getKeys?: (originalObject: Record) => string[]; + getKeys?: (originalObject: Record, depth: number) => string[]; } diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index cc3cbdf85367..7b832fb052e9 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -17,10 +17,13 @@ channel.on(STORY_RENDER_PHASE_CHANGED, ({ newPhase }) => { export const { expect } = instrument( { expect: rawExpect }, { - getKeys: (obj: Record) => { + getKeys: (obj: Record, depth) => { const privateApi = ['assert', '__methods', '__flags']; if (obj.constructor === chai.Assertion) { - return Object.keys(Object.getPrototypeOf(obj)).filter((it) => !privateApi.includes(it)); + const keys = Object.keys(Object.getPrototypeOf(obj)).filter( + (it) => !privateApi.includes(it) + ); + return depth > 2 ? keys : [...keys, 'not']; } return Object.keys(obj); }, diff --git a/code/lib/test/src/testing-library.ts b/code/lib/test/src/testing-library.ts index ade77cdccfc2..f438a9b6e5bf 100644 --- a/code/lib/test/src/testing-library.ts +++ b/code/lib/test/src/testing-library.ts @@ -5,7 +5,6 @@ import * as domTestingLibrary from '@testing-library/dom'; import _userEvent from '@testing-library/user-event'; import dedent from 'ts-dedent'; import type { FireFunction, FireObject } from '@testing-library/dom/types/events'; -import type { BoundFunctions } from '@testing-library/dom'; import type { Promisify, PromisifyObject } from './utils'; type TestingLibraryDom = typeof domTestingLibrary; @@ -14,17 +13,10 @@ const testingLibrary = instrument( { ...domTestingLibrary }, { intercept: (method, path) => - path[0] === 'fireEvent' || - method.startsWith('find') || - method.startsWith('waitFor') || - method.startsWith('query') || - method.startsWith('get'), + path[0] === 'fireEvent' || method.startsWith('find') || method.startsWith('waitFor'), } -) as {} as PromisifyObject> & { +) as {} as PromisifyObject> & { fireEvent: Promisify & PromisifyObject; - within: ( - element: HTMLElement - ) => PromisifyObject>; }; testingLibrary.screen = new Proxy(testingLibrary.screen, { @@ -105,12 +97,11 @@ export const { queryByText, queryByTitle, queryHelpers, + screen, waitFor, waitForElementToBeRemoved, within, prettyFormat, } = testingLibrary; -export const screen = testingLibrary.screen; - export const { userEvent } = instrument({ userEvent: _userEvent }, { intercept: true }); From ea2c3c36d9c22d4c90d46434631088ff154f6831 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Thu, 21 Sep 2023 14:57:17 +0200 Subject: [PATCH 11/36] fix yarn lock --- code/lib/test/package.json | 2 +- code/yarn.lock | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 38deca4b37bd..f5b45934111d 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -45,8 +45,8 @@ "dependencies": { "@storybook/client-logger": "workspace:^", "@storybook/core-events": "workspace:^", - "@storybook/preview-api": "workspace:^", "@storybook/instrumenter": "workspace:^", + "@storybook/preview-api": "workspace:^", "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^6.1.3", "@testing-library/user-event": "^14.4.3", diff --git a/code/yarn.lock b/code/yarn.lock index 46dc57b94071..9e190072ad23 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6865,7 +6865,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/core-events@workspace:*, @storybook/core-events@workspace:lib/core-events": +"@storybook/core-events@workspace:*, @storybook/core-events@workspace:^, @storybook/core-events@workspace:lib/core-events": version: 0.0.0-use.local resolution: "@storybook/core-events@workspace:lib/core-events" dependencies: @@ -7616,7 +7616,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/preview-api@workspace:*, @storybook/preview-api@workspace:lib/preview-api": +"@storybook/preview-api@workspace:*, @storybook/preview-api@workspace:^, @storybook/preview-api@workspace:lib/preview-api": version: 0.0.0-use.local resolution: "@storybook/preview-api@workspace:lib/preview-api" dependencies: @@ -8152,7 +8152,9 @@ __metadata: resolution: "@storybook/test@workspace:lib/test" dependencies: "@storybook/client-logger": "workspace:^" + "@storybook/core-events": "workspace:^" "@storybook/instrumenter": "workspace:^" + "@storybook/preview-api": "workspace:^" "@testing-library/dom": ^9.3.1 "@testing-library/jest-dom": ^6.1.3 "@testing-library/user-event": ^14.4.3 From 33cbcae5d6ddc37b6ad60f26d44ca4a4230b47e4 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 21 Sep 2023 17:14:38 +0200 Subject: [PATCH 12/36] Fix eslint --- code/lib/test/src/testing-library.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/test/src/testing-library.ts b/code/lib/test/src/testing-library.ts index f438a9b6e5bf..eb65fb1dcccf 100644 --- a/code/lib/test/src/testing-library.ts +++ b/code/lib/test/src/testing-library.ts @@ -1,4 +1,4 @@ -/* eslint-disable prefer-destructuring,@typescript-eslint/ban-types */ +/* eslint-disable @typescript-eslint/ban-types */ import { once } from '@storybook/client-logger'; import { instrument } from '@storybook/instrumenter'; import * as domTestingLibrary from '@testing-library/dom'; From 6d1986118f3814727f68dc8f02b5d8d17dc3ff2c Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 09:20:49 +0200 Subject: [PATCH 13/36] Comment out deprecation for now --- code/addons/actions/src/runtime/action.ts | 35 +++++++++++------------ 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/code/addons/actions/src/runtime/action.ts b/code/addons/actions/src/runtime/action.ts index 889e39405848..a0f6b1b08554 100644 --- a/code/addons/actions/src/runtime/action.ts +++ b/code/addons/actions/src/runtime/action.ts @@ -1,9 +1,5 @@ -/* eslint-disable no-underscore-dangle */ import { v4 as uuidv4 } from 'uuid'; -import type { PreviewWeb } from '@storybook/preview-api'; import { addons } from '@storybook/preview-api'; -import type { Renderer } from '@storybook/types'; -import { global } from '@storybook/global'; import { EVENT_ID } from '../constants'; import type { ActionDisplay, ActionOptions, HandlerFunction } from '../models'; import { config } from './configureActions'; @@ -50,21 +46,22 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti }; const handler = function actionHandler(...args: any[]) { - if (options.implicit) { - const preview = - '__STORYBOOK_PREVIEW__' in global - ? (global.__STORYBOOK_PREVIEW__ as PreviewWeb) - : undefined; - if ( - preview?.storyRenders.some( - (render) => render.phase === 'playing' || render.phase === 'rendering' - ) - ) { - console.warn( - 'Can not use implicit actions during rendering or playing of a story. \nSee: [docs page]' - ); - } - } + // TODO: Enable once codemods are finished + // if (options.implicit) { + // const preview = + // '__STORYBOOK_PREVIEW__' in global + // ? (global.__STORYBOOK_PREVIEW__ as PreviewWeb) + // : undefined; + // if ( + // preview?.storyRenders.some( + // (render) => render.phase === 'playing' || render.phase === 'rendering' + // ) + // ) { + // console.warn( + // 'Can not use implicit actions during rendering or playing of a story.' + // ); + // } + // } const channel = addons.getChannel(); const id = uuidv4(); From 0b7671618d7afc4b8d9dcb0d65292409273951c9 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 09:37:33 +0200 Subject: [PATCH 14/36] Fix types --- code/lib/test/package.json | 2 +- code/lib/test/src/testing-library.ts | 3 ++- code/lib/test/src/utils.ts | 2 +- code/yarn.lock | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/code/lib/test/package.json b/code/lib/test/package.json index f5b45934111d..079153c58365 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -58,7 +58,7 @@ }, "devDependencies": { "@types/chai": "^4", - "type-fest": "~2.19", + "type-fest": "^2.19.0", "typescript": "~4.9.3" }, "publishConfig": { diff --git a/code/lib/test/src/testing-library.ts b/code/lib/test/src/testing-library.ts index eb65fb1dcccf..ccac1f448923 100644 --- a/code/lib/test/src/testing-library.ts +++ b/code/lib/test/src/testing-library.ts @@ -5,6 +5,7 @@ import * as domTestingLibrary from '@testing-library/dom'; import _userEvent from '@testing-library/user-event'; import dedent from 'ts-dedent'; import type { FireFunction, FireObject } from '@testing-library/dom/types/events'; +import type { Writable } from 'type-fest'; import type { Promisify, PromisifyObject } from './utils'; type TestingLibraryDom = typeof domTestingLibrary; @@ -15,7 +16,7 @@ const testingLibrary = instrument( intercept: (method, path) => path[0] === 'fireEvent' || method.startsWith('find') || method.startsWith('waitFor'), } -) as {} as PromisifyObject> & { +) as {} as Writable> & { fireEvent: Promisify & PromisifyObject; }; diff --git a/code/lib/test/src/utils.ts b/code/lib/test/src/utils.ts index 53029f3c5a00..6f093cd0b9f4 100644 --- a/code/lib/test/src/utils.ts +++ b/code/lib/test/src/utils.ts @@ -2,4 +2,4 @@ export type Promisify = Fn extends (...args: infer A) => infer R ? (...args: A) => R extends Promise ? R : Promise : Fn; -export type PromisifyObject = { -readonly [K in keyof O]: Promisify }; +export type PromisifyObject = { [K in keyof O]: Promisify }; diff --git a/code/yarn.lock b/code/yarn.lock index 9e190072ad23..19d39aa333bf 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -8164,7 +8164,7 @@ __metadata: chai: ^4.3.7 expect: ^29.6.2 ts-dedent: ^2.2.0 - type-fest: ~2.19 + type-fest: ^4.3.1 typescript: ~4.9.3 languageName: unknown linkType: soft From da91e3dd85916f97d5b3403069d78c62edca5948 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 12:00:50 +0200 Subject: [PATCH 15/36] Make getters lazy and fix workspace --- code/lib/instrumenter/src/instrumenter.ts | 14 ++++++-------- code/lib/test/package.json | 8 ++++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 93fce9c957cd..c9ed517e054a 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -303,19 +303,17 @@ export class Instrumenter { return keys.reduce( (acc, key) => { const value = (obj as Record)[key]; - // Nothing to patch, but might be instrumentable, so we recurse if (typeof value !== 'function') { - const instrumented = this.instrument( - value, - { ...options, path: path.concat(key) }, - depth - ); const descriptor = getPropertyDescriptor(obj, key); if (typeof descriptor?.get === 'function') { - Object.defineProperty(acc, key, { get: () => instrumented }); + Object.defineProperty(acc, key, { + get: () => { + return this.instrument(value, { ...options, path: path.concat(key) }, depth); + }, + }); } else { - acc[key] = instrumented; + acc[key] = this.instrument(value, { ...options, path: path.concat(key) }, depth); } return acc; } diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 079153c58365..167506f544da 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -43,10 +43,10 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/client-logger": "workspace:^", - "@storybook/core-events": "workspace:^", - "@storybook/instrumenter": "workspace:^", - "@storybook/preview-api": "workspace:^", + "@storybook/client-logger": "workspace:*", + "@storybook/core-events": "workspace:*", + "@storybook/instrumenter": "workspace:*", + "@storybook/preview-api": "workspace:*", "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^6.1.3", "@testing-library/user-event": "^14.4.3", From 17b3a3312c68a0caae044c6c274fb97de682c07c Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 12:04:05 +0200 Subject: [PATCH 16/36] Fix yarn.lock --- code/yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/yarn.lock b/code/yarn.lock index 19d39aa333bf..11eb4211c578 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6739,7 +6739,7 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@workspace:*, @storybook/client-logger@workspace:^, @storybook/client-logger@workspace:lib/client-logger": +"@storybook/client-logger@workspace:*, @storybook/client-logger@workspace:lib/client-logger": version: 0.0.0-use.local resolution: "@storybook/client-logger@workspace:lib/client-logger" dependencies: @@ -6865,7 +6865,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/core-events@workspace:*, @storybook/core-events@workspace:^, @storybook/core-events@workspace:lib/core-events": +"@storybook/core-events@workspace:*, @storybook/core-events@workspace:lib/core-events": version: 0.0.0-use.local resolution: "@storybook/core-events@workspace:lib/core-events" dependencies: @@ -7157,7 +7157,7 @@ __metadata: languageName: node linkType: hard -"@storybook/instrumenter@workspace:*, @storybook/instrumenter@workspace:^, @storybook/instrumenter@workspace:lib/instrumenter": +"@storybook/instrumenter@workspace:*, @storybook/instrumenter@workspace:lib/instrumenter": version: 0.0.0-use.local resolution: "@storybook/instrumenter@workspace:lib/instrumenter" dependencies: @@ -7616,7 +7616,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/preview-api@workspace:*, @storybook/preview-api@workspace:^, @storybook/preview-api@workspace:lib/preview-api": +"@storybook/preview-api@workspace:*, @storybook/preview-api@workspace:lib/preview-api": version: 0.0.0-use.local resolution: "@storybook/preview-api@workspace:lib/preview-api" dependencies: @@ -8151,10 +8151,10 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/test@workspace:lib/test" dependencies: - "@storybook/client-logger": "workspace:^" - "@storybook/core-events": "workspace:^" - "@storybook/instrumenter": "workspace:^" - "@storybook/preview-api": "workspace:^" + "@storybook/client-logger": "workspace:*" + "@storybook/core-events": "workspace:*" + "@storybook/instrumenter": "workspace:*" + "@storybook/preview-api": "workspace:*" "@testing-library/dom": ^9.3.1 "@testing-library/jest-dom": ^6.1.3 "@testing-library/user-event": ^14.4.3 @@ -8164,7 +8164,7 @@ __metadata: chai: ^4.3.7 expect: ^29.6.2 ts-dedent: ^2.2.0 - type-fest: ^4.3.1 + type-fest: ^2.19.0 typescript: ~4.9.3 languageName: unknown linkType: soft From fae8299dd244ab2763ffa03fc9c73ac78232d0f9 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 12:26:39 +0200 Subject: [PATCH 17/36] Fix type fest range --- code/lib/test/package.json | 2 +- code/yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 167506f544da..b19dc90932a9 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -58,7 +58,7 @@ }, "devDependencies": { "@types/chai": "^4", - "type-fest": "^2.19.0", + "type-fest": "~2.19", "typescript": "~4.9.3" }, "publishConfig": { diff --git a/code/yarn.lock b/code/yarn.lock index 11eb4211c578..5096467d8d1f 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -8164,7 +8164,7 @@ __metadata: chai: ^4.3.7 expect: ^29.6.2 ts-dedent: ^2.2.0 - type-fest: ^2.19.0 + type-fest: ~2.19 typescript: ~4.9.3 languageName: unknown linkType: soft From 2085dd4394eacb0b77d602d9792e315bfaad2851 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 16:45:54 +0200 Subject: [PATCH 18/36] Patch vitest to make it compatible with the jest runner --- .../@vitest-expect-npm-0.34.3-313878cdb4.patch | 15 +++++++++++++++ code/jest.config.base.js | 1 + code/lib/test/package.json | 8 +++++--- code/package.json | 3 ++- code/yarn.lock | 15 ++++++++++++++- scripts/package.json | 1 + 6 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch diff --git a/code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch b/code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch new file mode 100644 index 000000000000..c9320c82c04c --- /dev/null +++ b/code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch @@ -0,0 +1,15 @@ +diff --git a/dist/index.js b/dist/index.js +index 9fd849550098f37f44ac6f9f64ad174cc821759a..44afa9ccbc42dd81e07ffa794b4be89c61d00c37 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -6,7 +6,9 @@ import { isMockFunction } from '@vitest/spy'; + import { processError } from '@vitest/utils/error'; + + const MATCHERS_OBJECT = Symbol.for("matchers-object"); +-const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object"); ++// Patched this symbol for storybook, so that @storybook/test can be used in a jest environment as well. ++// Otherwise, vitest will override global jest matchers, and crash. ++const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object-storybook"); + const GLOBAL_EXPECT = Symbol.for("expect-global"); + + if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) { diff --git a/code/jest.config.base.js b/code/jest.config.base.js index 9fdc75f588a9..59cb8cf3c37e 100644 --- a/code/jest.config.base.js +++ b/code/jest.config.base.js @@ -23,6 +23,7 @@ const modulesToTransform = [ '@angular', '@lit', '@mdx-js', + '@vitest', 'ccount', 'character-entities', 'decode-named-character-reference', diff --git a/code/lib/test/package.json b/code/lib/test/package.json index b19dc90932a9..728bb3674f35 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -50,16 +50,18 @@ "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^6.1.3", "@testing-library/user-event": "^14.4.3", - "@vitest/expect": "^0.34.2", - "@vitest/spy": "^0.34.1", "chai": "^4.3.7", "expect": "^29.6.2", + "loupe": "^2.3.1", "ts-dedent": "^2.2.0" }, "devDependencies": { "@types/chai": "^4", + "@vitest/expect": "^0.34.2", + "@vitest/spy": "^0.34.1", "type-fest": "~2.19", - "typescript": "~4.9.3" + "typescript": "~4.9.3", + "util": "^0.12.4" }, "publishConfig": { "access": "public" diff --git a/code/package.json b/code/package.json index c1ac7e210b62..cea806bec2a4 100644 --- a/code/package.json +++ b/code/package.json @@ -88,7 +88,8 @@ "playwright": "1.36.0", "playwright-core": "1.36.0", "serialize-javascript": "^3.1.0", - "type-fest": "~2.19" + "type-fest": "~2.19", + "@vitest/expect@^0.34.2": "patch:@vitest/expect@npm%3A0.34.3#./.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch" }, "dependencies": { "@babel/core": "^7.22.9", diff --git a/code/yarn.lock b/code/yarn.lock index 5096467d8d1f..e23a148e965d 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -8163,9 +8163,11 @@ __metadata: "@vitest/spy": ^0.34.1 chai: ^4.3.7 expect: ^29.6.2 + loupe: ^2.3.1 ts-dedent: ^2.2.0 type-fest: ~2.19 typescript: ~4.9.3 + util: ^0.12.4 languageName: unknown linkType: soft @@ -10205,7 +10207,7 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:^0.34.2": +"@vitest/expect@npm:0.34.3": version: 0.34.3 resolution: "@vitest/expect@npm:0.34.3" dependencies: @@ -10216,6 +10218,17 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@patch:@vitest/expect@npm%3A0.34.3#./.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch::locator=%40storybook%2Froot%40workspace%3A.": + version: 0.34.3 + resolution: "@vitest/expect@patch:@vitest/expect@npm%3A0.34.3#./.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch::version=0.34.3&hash=2c0ce2&locator=%40storybook%2Froot%40workspace%3A." + dependencies: + "@vitest/spy": 0.34.3 + "@vitest/utils": 0.34.3 + chai: ^4.3.7 + checksum: 6800fea8b719928002ba7b87f92e07f67ab9f21da38fb8e4e1ef6e27ea6e37f3418ea72f2d828706558e921ae8f63c6460b97cdc1ed276b8b7c6ff66889001d7 + languageName: node + linkType: hard + "@vitest/spy@npm:0.34.3, @vitest/spy@npm:^0.34.1": version: 0.34.3 resolution: "@vitest/spy@npm:0.34.3" diff --git a/scripts/package.json b/scripts/package.json index 906bb2b95926..e9a7bb490fc4 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -89,6 +89,7 @@ "@types/express": "^4.17.11", "@types/fs-extra": "^11.0.1", "@types/http-server": "^0.12.1", + "@types/jest": "^29.5.5", "@types/lodash": "^4", "@types/node": "^16.0.0", "@types/node-fetch": "^2.5.7", From 39ed28da9ee4a99fb4cdfaddd226826d494d3216 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 16:55:58 +0200 Subject: [PATCH 19/36] Add jest types to lock --- scripts/yarn.lock | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/yarn.lock b/scripts/yarn.lock index ee273eaf2d45..1fd9dc94ad92 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -2922,6 +2922,7 @@ __metadata: "@types/express": ^4.17.11 "@types/fs-extra": ^11.0.1 "@types/http-server": ^0.12.1 + "@types/jest": ^29.5.5 "@types/lodash": ^4 "@types/node": ^16.0.0 "@types/node-fetch": ^2.5.7 @@ -3567,6 +3568,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^29.5.5": + version: 29.5.5 + resolution: "@types/jest@npm:29.5.5" + dependencies: + expect: ^29.0.0 + pretty-format: ^29.0.0 + checksum: 0a3481f119099e6a0a381fec0d410cd33241267a0981576a7a832687fc3f888f79285289dc7c054c3589fd443f7ed1598d25fa7bc9708491b58da17e423b4aff + languageName: node + linkType: hard + "@types/jsdom@npm:^20.0.0": version: 20.0.1 resolution: "@types/jsdom@npm:20.0.1" From ddd8bf1e1c553af80cc258db12db094f748eab33 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 20:42:39 +0200 Subject: [PATCH 20/36] Better patch --- ...@vitest-expect-npm-0.34.3-313878cdb4.patch | 15 -------- code/package.json | 2 +- code/yarn.lock | 34 +++++++++---------- 3 files changed, 17 insertions(+), 34 deletions(-) delete mode 100644 code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch diff --git a/code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch b/code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch deleted file mode 100644 index c9320c82c04c..000000000000 --- a/code/.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/dist/index.js b/dist/index.js -index 9fd849550098f37f44ac6f9f64ad174cc821759a..44afa9ccbc42dd81e07ffa794b4be89c61d00c37 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -6,7 +6,9 @@ import { isMockFunction } from '@vitest/spy'; - import { processError } from '@vitest/utils/error'; - - const MATCHERS_OBJECT = Symbol.for("matchers-object"); --const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object"); -+// Patched this symbol for storybook, so that @storybook/test can be used in a jest environment as well. -+// Otherwise, vitest will override global jest matchers, and crash. -+const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object-storybook"); - const GLOBAL_EXPECT = Symbol.for("expect-global"); - - if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) { diff --git a/code/package.json b/code/package.json index cea806bec2a4..e04af9bd2da7 100644 --- a/code/package.json +++ b/code/package.json @@ -89,7 +89,7 @@ "playwright-core": "1.36.0", "serialize-javascript": "^3.1.0", "type-fest": "~2.19", - "@vitest/expect@^0.34.2": "patch:@vitest/expect@npm%3A0.34.3#./.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch" + "@vitest/expect@^0.34.2": "patch:@vitest/expect@npm%3A0.34.5#./.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch" }, "dependencies": { "@babel/core": "^7.22.9", diff --git a/code/yarn.lock b/code/yarn.lock index e23a148e965d..889b85d0ed19 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -10207,29 +10207,27 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:0.34.3": - version: 0.34.3 - resolution: "@vitest/expect@npm:0.34.3" +"@vitest/expect@npm:^0.34.2": + version: 0.34.5 + resolution: "@vitest/expect@npm:0.34.5" dependencies: - "@vitest/spy": 0.34.3 - "@vitest/utils": 0.34.3 + "@vitest/spy": 0.34.5 + "@vitest/utils": 0.34.5 chai: ^4.3.7 - checksum: ec70fba265b5bbd31f9c06c60e504ee0f1394df0871a5bddbe0a6b0a2436705f5e41e183fcdec3dd8864b5432b14d4f3802b896ba625ae0a44b52a9a08ddbd73 + checksum: dc30a5e1f2732a1906df57f65381df1129dbf994496734c27e4a3f832852862501eaba1ec2987215ec12ee23a8f2ef1d8ff63c7cd5490046a7a26800da1adcb2 languageName: node linkType: hard -"@vitest/expect@patch:@vitest/expect@npm%3A0.34.3#./.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch::locator=%40storybook%2Froot%40workspace%3A.": - version: 0.34.3 - resolution: "@vitest/expect@patch:@vitest/expect@npm%3A0.34.3#./.yarn/patches/@vitest-expect-npm-0.34.3-313878cdb4.patch::version=0.34.3&hash=2c0ce2&locator=%40storybook%2Froot%40workspace%3A." +"@vitest/spy@npm:0.34.5": + version: 0.34.5 + resolution: "@vitest/spy@npm:0.34.5" dependencies: - "@vitest/spy": 0.34.3 - "@vitest/utils": 0.34.3 - chai: ^4.3.7 - checksum: 6800fea8b719928002ba7b87f92e07f67ab9f21da38fb8e4e1ef6e27ea6e37f3418ea72f2d828706558e921ae8f63c6460b97cdc1ed276b8b7c6ff66889001d7 + tinyspy: ^2.1.1 + checksum: bbee495ca6300f50dde6418d14db0d3281daf38df15abae95202ddef253d6dd8bedf9f4a79da5a2246d3758ab24aa737caccf752fabcd8ba902a4f14801c2a0c languageName: node linkType: hard -"@vitest/spy@npm:0.34.3, @vitest/spy@npm:^0.34.1": +"@vitest/spy@npm:^0.34.1": version: 0.34.3 resolution: "@vitest/spy@npm:0.34.3" dependencies: @@ -10238,14 +10236,14 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:0.34.3": - version: 0.34.3 - resolution: "@vitest/utils@npm:0.34.3" +"@vitest/utils@npm:0.34.5": + version: 0.34.5 + resolution: "@vitest/utils@npm:0.34.5" dependencies: diff-sequences: ^29.4.3 loupe: ^2.3.6 pretty-format: ^29.5.0 - checksum: 7ca2d82c4286b963b8d8004812464c81b42d73d027b739fa5b2f2cdfb2a14e409f8ad1da6c269ba7379c74a93bb0e81485b2040a18a06a886b49c4bef1e494ab + checksum: 99cc5974ada1dab2b02220005c0fc97147baba175601a0faa1b2b6687c7f579d21a401077377d6f759b3aa8a07dcc8851cdc3e07f9a550ec289286107487ac36 languageName: node linkType: hard From 27ded25db5df02be7f920e1697a6d255c8ac054e Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 22 Sep 2023 20:49:47 +0200 Subject: [PATCH 21/36] Fix lock --- code/yarn.lock | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/code/yarn.lock b/code/yarn.lock index 889b85d0ed19..7a4687c53d1e 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -10207,7 +10207,7 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:^0.34.2": +"@vitest/expect@npm:0.34.5": version: 0.34.5 resolution: "@vitest/expect@npm:0.34.5" dependencies: @@ -10218,6 +10218,17 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@patch:@vitest/expect@npm%3A0.34.5#./.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch::locator=%40storybook%2Froot%40workspace%3A.": + version: 0.34.5 + resolution: "@vitest/expect@patch:@vitest/expect@npm%3A0.34.5#./.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch::version=0.34.5&hash=f89b80&locator=%40storybook%2Froot%40workspace%3A." + dependencies: + "@vitest/spy": 0.34.5 + "@vitest/utils": 0.34.5 + chai: ^4.3.7 + checksum: b08f0b1df6a37305f3f68feec15cfac048ca9e3924998698625394296faac4e539e23d7422eec59c0850a83b7342b574a2d2d174aaa33a7eb0004e4e366c515c + languageName: node + linkType: hard + "@vitest/spy@npm:0.34.5": version: 0.34.5 resolution: "@vitest/spy@npm:0.34.5" From d44e0bddb5c9e5684019e824526fd3726dc1ef03 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Sat, 23 Sep 2023 15:43:01 +0200 Subject: [PATCH 22/36] Fix lazy getters --- code/lib/instrumenter/src/instrumenter.ts | 26 ++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index c9ed517e054a..816e44cc0e66 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -302,19 +302,25 @@ export class Instrumenter { depth += 1; return keys.reduce( (acc, key) => { + const descriptor = getPropertyDescriptor(obj, key); + if (typeof descriptor?.get === 'function') { + Object.defineProperty(acc, key, { + get: () => { + return this.instrument( + (obj as Record)[key], + { ...options, path: path.concat(key) }, + depth + ); + }, + }); + return acc; + } + const value = (obj as Record)[key]; + // Nothing to patch, but might be instrumentable, so we recurse if (typeof value !== 'function') { - const descriptor = getPropertyDescriptor(obj, key); - if (typeof descriptor?.get === 'function') { - Object.defineProperty(acc, key, { - get: () => { - return this.instrument(value, { ...options, path: path.concat(key) }, depth); - }, - }); - } else { - acc[key] = this.instrument(value, { ...options, path: path.concat(key) }, depth); - } + acc[key] = this.instrument(value, { ...options, path: path.concat(key) }, depth); return acc; } From 8657c59aa8d96c74737202a37efee8b8e620618f Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Sat, 23 Sep 2023 15:51:07 +0200 Subject: [PATCH 23/36] Fix gitignore --- .gitignore | 1 + ...@vitest-expect-npm-0.34.5-8031508efe.patch | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 code/.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch diff --git a/.gitignore b/.gitignore index 63f9445af854..0d0e9ac8d8ce 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ test-results !/**/.yarn/plugins !/**/.yarn/sdks !/**/.yarn/versions +!/**/.yarn/patches /**/.pnp.* !/node_modules diff --git a/code/.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch b/code/.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch new file mode 100644 index 000000000000..175c8fbcc343 --- /dev/null +++ b/code/.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch @@ -0,0 +1,37 @@ +diff --git a/dist/index.js b/dist/index.js +index 5a61947ad50426d27390b4e82533179323ad3ba1..32bfc45909b645cb31cec2e204c8baa23f21fdd2 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -6,23 +6,29 @@ import { processError } from '@vitest/utils/error'; + import { util } from 'chai'; + + const MATCHERS_OBJECT = Symbol.for("matchers-object"); +-const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object"); ++// Patched this symbol for storybook, so that @storybook/test can be used in a jest environment as well. ++// Otherwise, vitest will override global jest matchers, and crash. ++const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object-storybook"); + const GLOBAL_EXPECT = Symbol.for("expect-global"); + + if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) { + const globalState = /* @__PURE__ */ new WeakMap(); +- const matchers = /* @__PURE__ */ Object.create(null); + Object.defineProperty(globalThis, MATCHERS_OBJECT, { + get: () => globalState + }); ++} ++ ++if (!Object.prototype.hasOwnProperty.call(globalThis, JEST_MATCHERS_OBJECT)) { ++ const matchers = /* @__PURE__ */ Object.create(null); + Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, { + configurable: true, + get: () => ({ +- state: globalState.get(globalThis[GLOBAL_EXPECT]), ++ state: globalThis[MATCHERS_OBJECT].get(globalThis[GLOBAL_EXPECT]), + matchers + }) + }); + } ++ + function getState(expect) { + return globalThis[MATCHERS_OBJECT].get(expect); + } From a5cdd3194b4f07fb8cbeeeab64970250a9d224ee Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 3 Oct 2023 11:36:26 +0200 Subject: [PATCH 24/36] Fix getter --- code/lib/instrumenter/src/instrumenter.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 816e44cc0e66..47d501770d5b 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -304,13 +304,10 @@ export class Instrumenter { (acc, key) => { const descriptor = getPropertyDescriptor(obj, key); if (typeof descriptor?.get === 'function') { + const getter = () => descriptor?.get?.(); Object.defineProperty(acc, key, { get: () => { - return this.instrument( - (obj as Record)[key], - { ...options, path: path.concat(key) }, - depth - ); + return this.instrument(getter(), { ...options, path: path.concat(key) }, depth); }, }); return acc; From de86f5d737d9a2b0875daf186dbc372acdcbbec2 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 4 Oct 2023 12:38:14 +0200 Subject: [PATCH 25/36] Always externalize @vitest deps in tsup, as they are ESM-only. But still use it for the dts files, as bundling dts files is buggy --- code/lib/test/package.json | 13 ++++++------- code/yarn.lock | 1 - scripts/prepare/bundle.ts | 4 ++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 728bb3674f35..7de3167917c8 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -50,18 +50,17 @@ "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^6.1.3", "@testing-library/user-event": "^14.4.3", + "@types/chai": "^4", + "@vitest/expect": "^0.34.2", + "@vitest/spy": "^0.34.1", "chai": "^4.3.7", "expect": "^29.6.2", - "loupe": "^2.3.1", - "ts-dedent": "^2.2.0" + "ts-dedent": "^2.2.0", + "util": "^0.12.4" }, "devDependencies": { - "@types/chai": "^4", - "@vitest/expect": "^0.34.2", - "@vitest/spy": "^0.34.1", "type-fest": "~2.19", - "typescript": "~4.9.3", - "util": "^0.12.4" + "typescript": "~4.9.3" }, "publishConfig": { "access": "public" diff --git a/code/yarn.lock b/code/yarn.lock index 78fecc29b567..4bc78dab3ce9 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -8122,7 +8122,6 @@ __metadata: "@vitest/spy": ^0.34.1 chai: ^4.3.7 expect: ^29.6.2 - loupe: ^2.3.1 ts-dedent: ^2.2.0 type-fest: ~2.19 typescript: ~4.9.3 diff --git a/scripts/prepare/bundle.ts b/scripts/prepare/bundle.ts index a15f71cd6a34..02199ecff992 100755 --- a/scripts/prepare/bundle.ts +++ b/scripts/prepare/bundle.ts @@ -79,9 +79,12 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { */ const nonPresetEntries = allEntries.filter((f) => !path.parse(f).name.includes('preset')); + const noExternal = [/^@vitest\/.+$/]; + if (formats.includes('esm')) { tasks.push( build({ + noExternal, silent: true, treeshake: true, entry: nonPresetEntries, @@ -116,6 +119,7 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { if (formats.includes('cjs')) { tasks.push( build({ + noExternal, silent: true, entry: allEntries, watch, From 6ee27f2b7d52bb55bf120ef0f2953eb74d44d6f6 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 3 Oct 2023 15:17:26 +0200 Subject: [PATCH 26/36] Always install extra deps --- scripts/tasks/sandbox-parts.ts | 7 ++----- scripts/tasks/sandbox.ts | 10 +++++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index e6b1bc188fd4..393169b244f8 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -354,7 +354,7 @@ async function linkPackageStories( ); } -async function addExtraDependencies({ +export async function addExtraDependencies({ cwd, dryRun, debug, @@ -378,7 +378,7 @@ async function addExtraDependencies({ export const addStories: Task['run'] = async ( { sandboxDir, template, key }, - { addon: extraAddons, dryRun, debug, disableDocs } + { addon: extraAddons, disableDocs } ) => { logger.log('💃 adding stories'); const cwd = sandboxDir; @@ -516,9 +516,6 @@ export const addStories: Task['run'] = async ( } } - // Some addon stories require extra dependencies - await addExtraDependencies({ cwd, dryRun, debug }); - await writeConfig(mainConfig); }; diff --git a/scripts/tasks/sandbox.ts b/scripts/tasks/sandbox.ts index 24b946049631..30f78137b586 100644 --- a/scripts/tasks/sandbox.ts +++ b/scripts/tasks/sandbox.ts @@ -37,7 +37,9 @@ export const sandbox: Task = { await remove(details.sandboxDir); } - const { create, install, addStories, extendMain, init } = await import('./sandbox-parts'); + const { create, install, addStories, extendMain, init, addExtraDependencies } = await import( + './sandbox-parts' + ); let startTime = now(); await create(details, options); @@ -72,6 +74,12 @@ export const sandbox: Task = { await addStories(details, options); } + await addExtraDependencies({ + cwd: details.sandboxDir, + debug: options.debug, + dryRun: options.dryRun, + }); + await extendMain(details, options); logger.info(`✅ Storybook sandbox created at ${details.sandboxDir}`); From 47586d92d13168fa4cdceeb2f6738bb9633c39ec Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 4 Oct 2023 13:48:55 +0200 Subject: [PATCH 27/36] Fix getters that use `this` --- code/lib/instrumenter/src/instrumenter.ts | 2 +- code/lib/test/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts index 47d501770d5b..de7914f3d64a 100644 --- a/code/lib/instrumenter/src/instrumenter.ts +++ b/code/lib/instrumenter/src/instrumenter.ts @@ -304,7 +304,7 @@ export class Instrumenter { (acc, key) => { const descriptor = getPropertyDescriptor(obj, key); if (typeof descriptor?.get === 'function') { - const getter = () => descriptor?.get?.(); + const getter = () => descriptor?.get?.bind(obj)?.(); Object.defineProperty(acc, key, { get: () => { return this.instrument(getter(), { ...options, path: path.concat(key) }, depth); diff --git a/code/lib/test/src/index.ts b/code/lib/test/src/index.ts index 7b832fb052e9..34d59391676e 100644 --- a/code/lib/test/src/index.ts +++ b/code/lib/test/src/index.ts @@ -18,7 +18,7 @@ export const { expect } = instrument( { expect: rawExpect }, { getKeys: (obj: Record, depth) => { - const privateApi = ['assert', '__methods', '__flags']; + const privateApi = ['assert', '__methods', '__flags', '_obj']; if (obj.constructor === chai.Assertion) { const keys = Object.keys(Object.getPrototypeOf(obj)).filter( (it) => !privateApi.includes(it) From 4ae6a7765affd710609f5984ed862d32c5d6e2ef Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Wed, 4 Oct 2023 13:52:22 +0200 Subject: [PATCH 28/36] Fix deps issue --- code/ui/manager/package.json | 1 + code/yarn.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json index 36960bd3baa2..5aa73a6c5779 100644 --- a/code/ui/manager/package.json +++ b/code/ui/manager/package.json @@ -61,6 +61,7 @@ "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:*", "@storybook/router": "workspace:*", + "@storybook/test": "workspace:*", "@storybook/theming": "workspace:*", "@storybook/types": "workspace:*", "@testing-library/react": "^11.2.2", diff --git a/code/yarn.lock b/code/yarn.lock index 4bc78dab3ce9..9110bc9020c8 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -7212,6 +7212,7 @@ __metadata: "@storybook/global": ^5.0.0 "@storybook/manager-api": "workspace:*" "@storybook/router": "workspace:*" + "@storybook/test": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" "@testing-library/react": ^11.2.2 @@ -8106,7 +8107,7 @@ __metadata: languageName: unknown linkType: soft -"@storybook/test@workspace:lib/test": +"@storybook/test@workspace:*, @storybook/test@workspace:lib/test": version: 0.0.0-use.local resolution: "@storybook/test@workspace:lib/test" dependencies: From 6de0d4919974b7fe41befb01c8d8eda53364dedb Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 5 Oct 2023 15:40:55 +0200 Subject: [PATCH 29/36] Fix lock files --- code/yarn.lock | 196 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 189 insertions(+), 7 deletions(-) diff --git a/code/yarn.lock b/code/yarn.lock index 3ec46c6397a8..d879303d9c2a 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -24,7 +24,7 @@ __metadata: languageName: node linkType: hard -"@adobe/css-tools@npm:^4.0.1": +"@adobe/css-tools@npm:^4.0.1, @adobe/css-tools@npm:^4.3.0": version: 4.3.1 resolution: "@adobe/css-tools@npm:4.3.1" checksum: 05672719b544cc0c21ae3ed0eb6349bf458e9d09457578eeeb07cf0f696469ac6417e9c9be1b129e5d6a18098a061c1db55b2275591760ef30a79822436fcbfa @@ -7206,6 +7206,7 @@ __metadata: "@storybook/global": ^5.0.0 "@storybook/manager-api": "workspace:*" "@storybook/router": "workspace:*" + "@storybook/test": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" "@testing-library/react": ^11.2.2 @@ -8099,6 +8100,29 @@ __metadata: languageName: unknown linkType: soft +"@storybook/test@workspace:*, @storybook/test@workspace:lib/test": + version: 0.0.0-use.local + resolution: "@storybook/test@workspace:lib/test" + dependencies: + "@storybook/client-logger": "workspace:*" + "@storybook/core-events": "workspace:*" + "@storybook/instrumenter": "workspace:*" + "@storybook/preview-api": "workspace:*" + "@testing-library/dom": ^9.3.1 + "@testing-library/jest-dom": ^6.1.3 + "@testing-library/user-event": ^14.4.3 + "@types/chai": ^4 + "@vitest/expect": ^0.34.2 + "@vitest/spy": ^0.34.1 + chai: ^4.3.7 + expect: ^29.6.2 + ts-dedent: ^2.2.0 + type-fest: ~2.19 + typescript: ~4.9.3 + util: ^0.12.4 + languageName: unknown + linkType: soft + "@storybook/testing-library@npm:next": version: 0.2.2-next.0 resolution: "@storybook/testing-library@npm:0.2.2-next.0" @@ -8694,7 +8718,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:^9.0.0": +"@testing-library/dom@npm:^9.0.0, @testing-library/dom@npm:^9.3.1": version: 9.3.3 resolution: "@testing-library/dom@npm:9.3.3" dependencies: @@ -8727,6 +8751,36 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:^6.1.2, @testing-library/jest-dom@npm:^6.1.3": + version: 6.1.3 + resolution: "@testing-library/jest-dom@npm:6.1.3" + dependencies: + "@adobe/css-tools": ^4.3.0 + "@babel/runtime": ^7.9.2 + aria-query: ^5.0.0 + chalk: ^3.0.0 + css.escape: ^1.5.1 + dom-accessibility-api: ^0.5.6 + lodash: ^4.17.15 + redent: ^3.0.0 + peerDependencies: + "@jest/globals": ">= 28" + "@types/jest": ">= 28" + jest: ">= 28" + vitest: ">= 0.32" + peerDependenciesMeta: + "@jest/globals": + optional: true + "@types/jest": + optional: true + jest: + optional: true + vitest: + optional: true + checksum: 544e01939d3c14a3d44ae2e2bb9fe2a0cb5a9e4992ca2728f41188fb9fb2d56e25f1a2e1c12000be2a94d8da36cb220b24020e1b5c5c4c4bede9058a0d80583d + languageName: node + linkType: hard + "@testing-library/react@npm:^11.2.2": version: 11.2.7 resolution: "@testing-library/react@npm:11.2.7" @@ -8751,7 +8805,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/user-event@npm:^14.4.0": +"@testing-library/user-event@npm:^14.4.0, @testing-library/user-event@npm:^14.4.3": version: 14.5.1 resolution: "@testing-library/user-event@npm:14.5.1" peerDependencies: @@ -8928,6 +8982,13 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^4": + version: 4.3.6 + resolution: "@types/chai@npm:4.3.6" + checksum: 388af382b11453a69808800479dcaff0323a0d1e15df1619175ebd55b294d716d560058f560ed55434e8846af46f017d7d78544822571f6322d3fac6d5f8a29d + languageName: node + linkType: hard + "@types/cheerio@npm:^0.22.22": version: 0.22.32 resolution: "@types/cheerio@npm:0.22.32" @@ -10140,6 +10201,57 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:0.34.5": + version: 0.34.5 + resolution: "@vitest/expect@npm:0.34.5" + dependencies: + "@vitest/spy": 0.34.5 + "@vitest/utils": 0.34.5 + chai: ^4.3.7 + checksum: dc30a5e1f2732a1906df57f65381df1129dbf994496734c27e4a3f832852862501eaba1ec2987215ec12ee23a8f2ef1d8ff63c7cd5490046a7a26800da1adcb2 + languageName: node + linkType: hard + +"@vitest/expect@patch:@vitest/expect@npm%3A0.34.5#./.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch::locator=%40storybook%2Froot%40workspace%3A.": + version: 0.34.5 + resolution: "@vitest/expect@patch:@vitest/expect@npm%3A0.34.5#./.yarn/patches/@vitest-expect-npm-0.34.5-8031508efe.patch::version=0.34.5&hash=f89b80&locator=%40storybook%2Froot%40workspace%3A." + dependencies: + "@vitest/spy": 0.34.5 + "@vitest/utils": 0.34.5 + chai: ^4.3.7 + checksum: b08f0b1df6a37305f3f68feec15cfac048ca9e3924998698625394296faac4e539e23d7422eec59c0850a83b7342b574a2d2d174aaa33a7eb0004e4e366c515c + languageName: node + linkType: hard + +"@vitest/spy@npm:0.34.5": + version: 0.34.5 + resolution: "@vitest/spy@npm:0.34.5" + dependencies: + tinyspy: ^2.1.1 + checksum: bbee495ca6300f50dde6418d14db0d3281daf38df15abae95202ddef253d6dd8bedf9f4a79da5a2246d3758ab24aa737caccf752fabcd8ba902a4f14801c2a0c + languageName: node + linkType: hard + +"@vitest/spy@npm:^0.34.1": + version: 0.34.7 + resolution: "@vitest/spy@npm:0.34.7" + dependencies: + tinyspy: ^2.1.1 + checksum: 1150b270eb72a5e8e7da997bcba90ebe5ed2ac50de1ea1f81738e16a19ab4bc77ca4d17639988df65695d4b325fe3647a1e4204d01024bcf5ecac8ba7764a2cc + languageName: node + linkType: hard + +"@vitest/utils@npm:0.34.5": + version: 0.34.5 + resolution: "@vitest/utils@npm:0.34.5" + dependencies: + diff-sequences: ^29.4.3 + loupe: ^2.3.6 + pretty-format: ^29.5.0 + checksum: 99cc5974ada1dab2b02220005c0fc97147baba175601a0faa1b2b6687c7f579d21a401077377d6f759b3aa8a07dcc8851cdc3e07f9a550ec289286107487ac36 + languageName: node + linkType: hard + "@volar/language-core@npm:1.10.1, @volar/language-core@npm:~1.10.0": version: 1.10.1 resolution: "@volar/language-core@npm:1.10.1" @@ -11465,6 +11577,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: 25456b2aa333250f01143968e02e4884a34588a8538fbbf65c91a637f1dbfb8069249133cd2f4e530f10f624d206a664e7df30207830b659e9f5298b00a4099b + languageName: node + linkType: hard + "assign-symbols@npm:^1.0.0": version: 1.0.0 resolution: "assign-symbols@npm:1.0.0" @@ -12893,6 +13012,21 @@ __metadata: languageName: node linkType: hard +"chai@npm:^4.3.7": + version: 4.3.10 + resolution: "chai@npm:4.3.10" + dependencies: + assertion-error: ^1.1.0 + check-error: ^1.0.3 + deep-eql: ^4.1.3 + get-func-name: ^2.0.2 + loupe: ^2.3.6 + pathval: ^1.1.1 + type-detect: ^4.0.8 + checksum: c887d24f67be6fb554c7ebbde3bb0568697a8833d475e4768296916891ba143f25fc079f6eb34146f3dd5a3279d34c1f387c32c9a6ab288e579f948d9ccf53fe + languageName: node + linkType: hard + "chalk@npm:4.1.0": version: 4.1.0 resolution: "chalk@npm:4.1.0" @@ -13020,6 +13154,15 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^1.0.3": + version: 1.0.3 + resolution: "check-error@npm:1.0.3" + dependencies: + get-func-name: ^2.0.2 + checksum: 94aa37a7315c0e8a83d0112b5bfb5a8624f7f0f81057c73e4707729cdd8077166c6aefb3d8e2b92c63ee130d4a2ff94bad46d547e12f3238cc1d78342a973841 + languageName: node + linkType: hard + "checkup@npm:^1.3.0": version: 1.3.0 resolution: "checkup@npm:1.3.0" @@ -14517,6 +14660,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^4.1.3": + version: 4.1.3 + resolution: "deep-eql@npm:4.1.3" + dependencies: + type-detect: ^4.0.0 + checksum: ff34e8605d8253e1bf9fe48056e02c6f347b81d9b5df1c6650a1b0f6f847b4a86453b16dc226b34f853ef14b626e85d04e081b022e20b00cd7d54f079ce9bbdd + languageName: node + linkType: hard + "deep-equal@npm:^1.1.1": version: 1.1.1 resolution: "deep-equal@npm:1.1.1" @@ -14830,7 +14982,7 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.6.3": +"diff-sequences@npm:^29.4.3, diff-sequences@npm:^29.6.3": version: 29.6.3 resolution: "diff-sequences@npm:29.6.3" checksum: 32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 @@ -16696,7 +16848,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.7.0": +"expect@npm:^29.0.0, expect@npm:^29.6.2, expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" dependencies: @@ -17788,6 +17940,13 @@ __metadata: languageName: node linkType: hard +"get-func-name@npm:^2.0.0, get-func-name@npm:^2.0.2": + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": version: 1.2.1 resolution: "get-intrinsic@npm:1.2.1" @@ -22142,6 +22301,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^2.3.6": + version: 2.3.6 + resolution: "loupe@npm:2.3.6" + dependencies: + get-func-name: ^2.0.0 + checksum: a974841ce94ef2a35aac7144e7f9e789e3887f82286cd9ffe7ff00f2ac9d117481989948657465e2b0b102f23136d89ae0a18fd4a32d9015012cd64464453289 + languageName: node + linkType: hard + "lower-case@npm:^2.0.2": version: 2.0.2 resolution: "lower-case@npm:2.0.2" @@ -25622,6 +25790,13 @@ __metadata: languageName: node linkType: hard +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: f63e1bc1b33593cdf094ed6ff5c49c1c0dc5dc20a646ca9725cc7fe7cd9995002d51d5685b9b2ec6814342935748b711bafa840f84c0bb04e38ff40a335c94dc + languageName: node + linkType: hard + "pbkdf2@npm:^3.0.3": version: 3.1.2 resolution: "pbkdf2@npm:3.1.2" @@ -26223,7 +26398,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.5.0, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" dependencies: @@ -30430,6 +30605,13 @@ __metadata: languageName: node linkType: hard +"tinyspy@npm:^2.1.1": + version: 2.2.0 + resolution: "tinyspy@npm:2.2.0" + checksum: 8c7b70748dd8590e85d52741db79243746c15bc03c92d75c23160a762142db577e7f53e360ba7300e321b12bca5c42dd2522a8dbeec6ba3830302573dd8516bc + languageName: node + linkType: hard + "tmp@npm:0.0.28": version: 0.0.28 resolution: "tmp@npm:0.0.28" @@ -30881,7 +31063,7 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:4.0.8": +"type-detect@npm:4.0.8, type-detect@npm:^4.0.0, type-detect@npm:^4.0.8": version: 4.0.8 resolution: "type-detect@npm:4.0.8" checksum: 8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd From 2b9981a9193563a44ca69ce91bad3a5bc8db8996 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 5 Oct 2023 15:45:56 +0200 Subject: [PATCH 30/36] Fix check script --- code/ui/blocks/src/blocks/internal/InternalCanvas.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/code/ui/blocks/src/blocks/internal/InternalCanvas.stories.tsx b/code/ui/blocks/src/blocks/internal/InternalCanvas.stories.tsx index fb0f444720c3..b45015c94b50 100644 --- a/code/ui/blocks/src/blocks/internal/InternalCanvas.stories.tsx +++ b/code/ui/blocks/src/blocks/internal/InternalCanvas.stories.tsx @@ -1,5 +1,4 @@ /// ; -/// ; import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { userEvent, within } from '@storybook/testing-library'; From fb8461369a7ad77d9c27acb301bde53db4475ba6 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 10 Oct 2023 10:18:18 +0200 Subject: [PATCH 31/36] Calculate the diff for chai errors --- .../src/components/Interaction.tsx | 25 ++++++++++++++++++- .../src/components/MatcherResult.tsx | 9 ++++++- code/lib/instrumenter/package.json | 4 ++- code/lib/instrumenter/src/instrumenter.ts | 12 ++++++++- code/lib/instrumenter/src/types.ts | 4 +++ code/yarn.lock | 13 ++++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/code/addons/interactions/src/components/Interaction.tsx b/code/addons/interactions/src/components/Interaction.tsx index bacaea0fe358..af2bea6bd20b 100644 --- a/code/addons/interactions/src/components/Interaction.tsx +++ b/code/addons/interactions/src/components/Interaction.tsx @@ -4,7 +4,7 @@ import { type Call, CallStates, type ControlStates } from '@storybook/instrument import { styled, typography } from '@storybook/theming'; import { transparentize } from 'polished'; -import { MatcherResult } from './MatcherResult'; +import { Expected, MatcherResult, Received } from './MatcherResult'; import { MethodCall } from './MethodCall'; import { StatusIcon } from './StatusIcon'; @@ -120,6 +120,29 @@ const Exception = ({ exception }: { exception: Call['exception'] }) => { return (
{paragraphs[0]}
+ + {exception.showDiff && exception.diff ? ( + <> +
+ + + ) : ( +
+          
+ {exception.expected && ( + <> + Expected: +
+ + )} + {exception.actual && ( + <> + Received: +
+ + )} +
+ )} {more &&

See the full stack trace in the browser console.

}
); diff --git a/code/addons/interactions/src/components/MatcherResult.tsx b/code/addons/interactions/src/components/MatcherResult.tsx index a8a1e00a63f7..6f1d8aef9f1d 100644 --- a/code/addons/interactions/src/components/MatcherResult.tsx +++ b/code/addons/interactions/src/components/MatcherResult.tsx @@ -45,7 +45,13 @@ export const Expected = ({ value, parsed }: { value: any; parsed?: boolean }) => return {value}; }; -export const MatcherResult = ({ message }: { message: string }) => { +export const MatcherResult = ({ + message, + style = {}, +}: { + message: string; + style?: React.CSSProperties; +}) => { const lines = message.split('\n'); return (
 {
         margin: 0,
         padding: '8px 10px 8px 36px',
         fontSize: typography.size.s1,
+        ...style,
       }}
     >
       {lines.flatMap((line: string, index: number) => {
diff --git a/code/lib/instrumenter/package.json b/code/lib/instrumenter/package.json
index b9d22eb8445f..aae6b577f111 100644
--- a/code/lib/instrumenter/package.json
+++ b/code/lib/instrumenter/package.json
@@ -48,7 +48,9 @@
     "@storybook/client-logger": "workspace:*",
     "@storybook/core-events": "workspace:*",
     "@storybook/global": "^5.0.0",
-    "@storybook/preview-api": "workspace:*"
+    "@storybook/preview-api": "workspace:*",
+    "@vitest/utils": "^0.34.6",
+    "util": "^0.12.4"
   },
   "devDependencies": {
     "typescript": "~4.9.3"
diff --git a/code/lib/instrumenter/src/instrumenter.ts b/code/lib/instrumenter/src/instrumenter.ts
index de7914f3d64a..0ffaa0ca0d5f 100644
--- a/code/lib/instrumenter/src/instrumenter.ts
+++ b/code/lib/instrumenter/src/instrumenter.ts
@@ -10,6 +10,7 @@ import {
   STORY_RENDER_PHASE_CHANGED,
 } from '@storybook/core-events';
 import { global } from '@storybook/global';
+import { processError } from '@vitest/utils/error';
 
 import type { Call, CallRef, ControlStates, LogItem, Options, State, SyncPayload } from './types';
 import { CallStates } from './types';
@@ -466,7 +467,16 @@ export class Instrumenter {
     const handleException = (e: any) => {
       if (e instanceof Error) {
         const { name, message, stack, callId = call.id } = e as Error & { callId: Call['id'] };
-        const exception = { name, message, stack, callId };
+
+        // This will calculate the diff for chai errors
+        const {
+          showDiff = undefined,
+          diff = undefined,
+          actual = undefined,
+          expected = undefined,
+        } = processError(e);
+
+        const exception = { name, message, stack, callId, showDiff, diff, actual, expected };
         this.update({ ...info, status: CallStates.ERROR, exception });
 
         // Always track errors to their originating call.
diff --git a/code/lib/instrumenter/src/types.ts b/code/lib/instrumenter/src/types.ts
index 0a528a5c161a..f3a2ee274200 100644
--- a/code/lib/instrumenter/src/types.ts
+++ b/code/lib/instrumenter/src/types.ts
@@ -16,6 +16,10 @@ export interface Call {
     message: Error['message'];
     stack: Error['stack'];
     callId: Call['id'];
+    showDiff?: boolean;
+    diff?: string;
+    actual?: unknown;
+    expected?: unknown;
   };
 }
 
diff --git a/code/yarn.lock b/code/yarn.lock
index d879303d9c2a..90c5acfda7fb 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -7118,7 +7118,9 @@ __metadata:
     "@storybook/core-events": "workspace:*"
     "@storybook/global": ^5.0.0
     "@storybook/preview-api": "workspace:*"
+    "@vitest/utils": ^0.34.6
     typescript: ~4.9.3
+    util: ^0.12.4
   languageName: unknown
   linkType: soft
 
@@ -10252,6 +10254,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@vitest/utils@npm:^0.34.6":
+  version: 0.34.7
+  resolution: "@vitest/utils@npm:0.34.7"
+  dependencies:
+    diff-sequences: ^29.4.3
+    loupe: ^2.3.6
+    pretty-format: ^29.5.0
+  checksum: 5f26ec5b4a53709a50efdb57aa753e8090b3411e888774f67a0d192eb7f046ed5fcc6884eb3d6275d2674926e724b731e8d28cd3cea96a7f3d27462a0d44af9e
+  languageName: node
+  linkType: hard
+
 "@volar/language-core@npm:1.10.1, @volar/language-core@npm:~1.10.0":
   version: 1.10.1
   resolution: "@volar/language-core@npm:1.10.1"

From 6801e046e0e12a02132fcbe62deb99e30427d44b Mon Sep 17 00:00:00 2001
From: Kasper Peulen 
Date: Tue, 10 Oct 2023 11:58:55 +0200
Subject: [PATCH 32/36] Add moduleNameMapper for ESM only modules with package
 json exports

---
 code/jest.config.base.js                       | 2 ++
 code/lib/instrumenter/src/instrumenter.test.ts | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/code/jest.config.base.js b/code/jest.config.base.js
index 59cb8cf3c37e..89ff7acf0b8b 100644
--- a/code/jest.config.base.js
+++ b/code/jest.config.base.js
@@ -61,6 +61,8 @@ module.exports = {
       path.resolve('./__mocks__/fileMock.js'),
     '\\.(css|scss|stylesheet)$': path.resolve('./__mocks__/styleMock.js'),
     '\\.(md)$': path.resolve('./__mocks__/htmlMock.js'),
+    '@vitest/utils/(.*)': '@vitest/utils/dist/$1.js',
+    '@vitest/utils': '@vitest/utils/dist/index.js',
   },
   transform: {
     '^.+\\.(t|j)sx?$': ['@swc/jest', swcrc],
diff --git a/code/lib/instrumenter/src/instrumenter.test.ts b/code/lib/instrumenter/src/instrumenter.test.ts
index 184c6773a747..35f0b6a87830 100644
--- a/code/lib/instrumenter/src/instrumenter.test.ts
+++ b/code/lib/instrumenter/src/instrumenter.test.ts
@@ -548,12 +548,12 @@ describe('Instrumenter', () => {
       expect(callSpy).toHaveBeenCalledWith(
         expect.objectContaining({
           id: 'kind--story [0] fn',
-          exception: {
+          exception: expect.objectContaining({
             name: 'Error',
             message: 'Boom!',
             stack: expect.stringContaining('Error: Boom!'),
             callId: 'kind--story [0] fn',
-          },
+          }),
         })
       );
     });

From ef847b9bf1b7ec56cabfed1f431ab0e28dba2f47 Mon Sep 17 00:00:00 2001
From: Kasper Peulen 
Date: Tue, 10 Oct 2023 15:50:27 +0200
Subject: [PATCH 33/36] Downcast function types that are mocks in meta args

---
 code/renderers/react/package.json             |  1 +
 .../renderers/react/src/public-types.test.tsx | 28 +++++++++++++++++++
 code/renderers/react/src/public-types.ts      | 12 +++++++-
 code/yarn.lock                                |  1 +
 4 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json
index 75c51405d7e7..87a83b58abd5 100644
--- a/code/renderers/react/package.json
+++ b/code/renderers/react/package.json
@@ -77,6 +77,7 @@
   },
   "devDependencies": {
     "@babel/core": "^7.22.9",
+    "@storybook/test": "workspace:*",
     "@types/util-deprecate": "^1.0.0",
     "expect-type": "^0.15.0",
     "jest-specific-snapshot": "^8.0.0",
diff --git a/code/renderers/react/src/public-types.test.tsx b/code/renderers/react/src/public-types.test.tsx
index 1fc3fd15d10c..ea4beb7810c0 100644
--- a/code/renderers/react/src/public-types.test.tsx
+++ b/code/renderers/react/src/public-types.test.tsx
@@ -7,6 +7,8 @@ import type { KeyboardEventHandler, ReactNode } from 'react';
 import React from 'react';
 
 import type { SetOptional } from 'type-fest';
+import type { Mock } from '@storybook/test';
+import { fn } from '@storybook/test';
 
 import type { Decorator, Meta, StoryObj } from './public-types';
 import type { ReactRenderer } from './types';
@@ -300,3 +302,29 @@ test('Meta is broken when using discriminating types, issue #23629', () => {
     },
   }).toMatchTypeOf>();
 });
+
+test('Infer mock function given to args in meta.', () => {
+  type Props = { label: string; onClick: () => void; onRender: () => JSX.Element };
+  const TestButton = (props: Props) => <>;
+
+  const meta = {
+    component: TestButton,
+    args: { label: 'label', onClick: fn(), onRender: () => <>some jsx },
+  } satisfies Meta;
+
+  type Story = StoryObj;
+
+  const Basic: Story = {
+    play: async ({ args }) => {
+      expectTypeOf(args.onClick).toEqualTypeOf>();
+      expectTypeOf(args.onRender).toEqualTypeOf<() => JSX.Element>();
+    },
+  };
+  type Expected = StoryAnnotations<
+    ReactRenderer,
+    Props & { onClick: Mock<[], void> },
+    Partial
+  >;
+
+  expectTypeOf(Basic).toEqualTypeOf();
+});
diff --git a/code/renderers/react/src/public-types.ts b/code/renderers/react/src/public-types.ts
index 592b82b03ff9..95ad7111a3ea 100644
--- a/code/renderers/react/src/public-types.ts
+++ b/code/renderers/react/src/public-types.ts
@@ -56,7 +56,7 @@ export type StoryObj = [TMetaOrCmpOrArgs] extends [
     > extends infer TArgs
     ? StoryAnnotations<
         ReactRenderer,
-        TArgs,
+        AddMocks,
         SetOptional)>
       >
     : never
@@ -64,6 +64,16 @@ export type StoryObj = [TMetaOrCmpOrArgs] extends [
   ? StoryAnnotations>
   : StoryAnnotations;
 
+// This performs a downcast to function types that are mocks, when a mock fn is given to meta args.
+type AddMocks = Simplify<{
+  [T in keyof TArgs]: T extends keyof DefaultArgs
+    ? // eslint-disable-next-line @typescript-eslint/ban-types
+      DefaultArgs[T] extends (...args: any) => any & { mock: {} } // allow any function with a mock object
+      ? DefaultArgs[T]
+      : TArgs[T]
+    : TArgs[T];
+}>;
+
 type ActionArgs = {
   // This can be read as: filter TArgs on functions where we can assign a void function to that function.
   // The docs addon argsEnhancers can only safely provide a default value for void functions.
diff --git a/code/yarn.lock b/code/yarn.lock
index 90c5acfda7fb..da0b6702494c 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -7705,6 +7705,7 @@ __metadata:
     "@storybook/global": ^5.0.0
     "@storybook/preview-api": "workspace:*"
     "@storybook/react-dom-shim": "workspace:*"
+    "@storybook/test": "workspace:*"
     "@storybook/types": "workspace:*"
     "@types/escodegen": ^0.0.6
     "@types/estree": ^0.0.51

From f42e9efbba01ab40c5cdd770e6fc96aaf6ed189a Mon Sep 17 00:00:00 2001
From: Kasper Peulen 
Date: Wed, 25 Oct 2023 15:09:48 +0200
Subject: [PATCH 34/36] Pre bundle most deps of the test package

---
 code/lib/test/package.json | 9 +++++++--
 code/yarn.lock             | 3 +--
 scripts/prepare/bundle.ts  | 4 +++-
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/code/lib/test/package.json b/code/lib/test/package.json
index 7de3167917c8..73b04a6e593f 100644
--- a/code/lib/test/package.json
+++ b/code/lib/test/package.json
@@ -54,11 +54,10 @@
     "@vitest/expect": "^0.34.2",
     "@vitest/spy": "^0.34.1",
     "chai": "^4.3.7",
-    "expect": "^29.6.2",
-    "ts-dedent": "^2.2.0",
     "util": "^0.12.4"
   },
   "devDependencies": {
+    "ts-dedent": "^2.2.0",
     "type-fest": "~2.19",
     "typescript": "~4.9.3"
   },
@@ -68,6 +67,12 @@
   "bundler": {
     "entries": [
       "./src/index.ts"
+    ],
+    "noExternal": [
+      "@testing-library/dom",
+      "@testing-library/jest-dom",
+      "@testing-library/user-event",
+      "chai"
     ]
   },
   "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17"
diff --git a/code/yarn.lock b/code/yarn.lock
index 35eb213e84d6..778da8443e19 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -8166,7 +8166,6 @@ __metadata:
     "@vitest/expect": ^0.34.2
     "@vitest/spy": ^0.34.1
     chai: ^4.3.7
-    expect: ^29.6.2
     ts-dedent: ^2.2.0
     type-fest: ~2.19
     typescript: ~4.9.3
@@ -16910,7 +16909,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"expect@npm:^29.0.0, expect@npm:^29.6.2, expect@npm:^29.7.0":
+"expect@npm:^29.0.0, expect@npm:^29.7.0":
   version: 29.7.0
   resolution: "expect@npm:29.7.0"
   dependencies:
diff --git a/scripts/prepare/bundle.ts b/scripts/prepare/bundle.ts
index 02199ecff992..1228e445a034 100755
--- a/scripts/prepare/bundle.ts
+++ b/scripts/prepare/bundle.ts
@@ -16,6 +16,7 @@ type Formats = 'esm' | 'cjs';
 type BundlerConfig = {
   entries: string[];
   externals: string[];
+  noExternal: string[];
   platform: Options['platform'];
   pre: string;
   post: string;
@@ -36,6 +37,7 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
     bundler: {
       entries = [],
       externals: extraExternals = [],
+      noExternal: extraNoExternal = [],
       platform,
       pre,
       post,
@@ -79,7 +81,7 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
    */
   const nonPresetEntries = allEntries.filter((f) => !path.parse(f).name.includes('preset'));
 
-  const noExternal = [/^@vitest\/.+$/];
+  const noExternal = [/^@vitest\/.+$/, ...extraNoExternal];
 
   if (formats.includes('esm')) {
     tasks.push(

From bcf87ea500868f6702c7ae4723adcb51693a44c0 Mon Sep 17 00:00:00 2001
From: Kasper Peulen 
Date: Fri, 27 Oct 2023 10:45:33 +0200
Subject: [PATCH 35/36] Fix eslint

---
 code/addons/interactions/src/preview.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts
index cee79b7a2dd9..54c7c18faab5 100644
--- a/code/addons/interactions/src/preview.ts
+++ b/code/addons/interactions/src/preview.ts
@@ -39,7 +39,6 @@ const addSpies = (id: string, val: any, key?: string): any => {
     if (Array.isArray(val)) {
       return val.map((item, index) => addSpies(id, item, `${key}[${index}]`));
     }
-    // eslint-disable-next-line no-underscore-dangle
     if (typeof val === 'function' && val.isAction && !val._isMockFunction) {
       Object.defineProperty(val, 'name', { value: key, writable: false });
       Object.defineProperty(val, '__storyId__', { value: id, writable: false });

From d888f60d660459f825aab7660f76cebd171b6f9e Mon Sep 17 00:00:00 2001
From: Kasper Peulen 
Date: Fri, 27 Oct 2023 14:31:47 +0200
Subject: [PATCH 36/36] Fix next version

---
 code/frameworks/nextjs/package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json
index 76fdfcd405e1..e26588814fb0 100644
--- a/code/frameworks/nextjs/package.json
+++ b/code/frameworks/nextjs/package.json
@@ -124,8 +124,8 @@
     "webpack": "^5.65.0"
   },
   "peerDependencies": {
-    "@next/font": "^13.0.0",
-    "next": "^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0",
+    "@next/font": "^13.0.0 || ^14.0.0",
+    "next": "^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0",
     "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
     "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
     "webpack": "^5.0.0"