From 30de294cae6bee0874b416fe18d9988ef5cc70cf Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 11:42:57 +0200 Subject: [PATCH 01/11] Try another mock naming convention --- .../nextjs/src/export-mocks/cache/index.ts | 4 +- .../src/export-mocks/headers/cookies.ts | 112 ++---------------- .../src/export-mocks/headers/headers.ts | 98 ++------------- .../src/export-mocks/navigation/index.ts | 59 ++++----- .../nextjs/src/export-mocks/router/index.ts | 30 +++-- 5 files changed, 64 insertions(+), 239 deletions(-) diff --git a/code/frameworks/nextjs/src/export-mocks/cache/index.ts b/code/frameworks/nextjs/src/export-mocks/cache/index.ts index 1692979f015..b9f2fa8dd43 100644 --- a/code/frameworks/nextjs/src/export-mocks/cache/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/cache/index.ts @@ -3,8 +3,8 @@ import { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cac import { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store'; // mock utilities/overrides (as of Next v14.2.0) -const revalidatePath = fn().mockName('revalidatePath'); -const revalidateTag = fn().mockName('revalidateTag'); +const revalidatePath = fn().mockName('next/cache::revalidatePath'); +const revalidateTag = fn().mockName('next/cache::revalidateTag'); const cacheExports = { unstable_cache, diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts index 75022baaea2..c3272dc0e33 100644 --- a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts +++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts @@ -1,116 +1,22 @@ -/* eslint-disable no-underscore-dangle */ import { fn } from '@storybook/test'; -import type { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'; -import { - parseCookie, - stringifyCookie, - type RequestCookie, -} from 'next/dist/compiled/@edge-runtime/cookies'; +import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'; // We need this import to be a singleton, and because it's used in multiple entrypoints // both in ESM and CJS, importing it via the package name instead of having a local import // is the only way to achieve it actually being a singleton // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore we must ignore types here as during compilation they are not generated yet -import { headers, type HeadersStore } from '@storybook/nextjs/headers.mock'; +import { headers } from '@storybook/nextjs/headers.mock'; -const stringifyCookies = (map: Map) => { - return Array.from(map) - .map(([_, v]) => stringifyCookie(v).replace(/; /, '')) - .join('; '); -}; - -// Mostly copied from https://github.com/vercel/edge-runtime/blob/c25e2ded39104e2a3be82efc08baf8dc8fb436b3/packages/cookies/src/request-cookies.ts#L7 -class RequestCookiesMock implements RequestCookies { - /** @internal */ - private readonly _headers: HeadersStore; - - _parsed: Map = new Map(); - - constructor(requestHeaders: HeadersStore) { - this._headers = requestHeaders; - const header = requestHeaders?.get('cookie'); - if (header) { - const parsed = parseCookie(header); - for (const [name, value] of parsed) { - this._parsed.set(name, { name, value }); - } - } - } - - [Symbol.iterator]() { - return this._parsed[Symbol.iterator](); - } - - get size(): number { - return this._parsed.size; - } +class RequestCookiesMock extends RequestCookies { + get = fn(super.get).mockName('next/headers::get'); - get = fn((...args: [name: string] | [RequestCookie]) => { - const name = typeof args[0] === 'string' ? args[0] : args[0].name; - return this._parsed.get(name); - }).mockName('cookies().get'); + getAll = fn(super.getAll).mockName('next/headers::cookies().getAll'); - getAll = fn((...args: [name: string] | [RequestCookie] | []) => { - const all = Array.from(this._parsed); - if (!args.length) { - return all.map(([_, value]) => value); - } + has = fn(super.has).mockName('next/headers::cookies().has'); - const name = typeof args[0] === 'string' ? args[0] : args[0]?.name; - return all.filter(([n]) => n === name).map(([_, value]) => value); - }).mockName('cookies().getAll'); + set = fn(super.set).mockName('next/headers::cookies().set'); - has = fn((name: string) => { - return this._parsed.has(name); - }).mockName('cookies().has'); - - set = fn((...args: [key: string, value: string] | [options: RequestCookie]): this => { - const [name, value] = args.length === 1 ? [args[0].name, args[0].value] : args; - - const map = this._parsed; - map.set(name, { name, value }); - - this._headers.set('cookie', stringifyCookies(map)); - return this; - }).mockName('cookies().set'); - - /** - * Delete the cookies matching the passed name or names in the request. - */ - delete = fn( - ( - /** Name or names of the cookies to be deleted */ - names: string | string[] - ): boolean | boolean[] => { - const map = this._parsed; - const result = !Array.isArray(names) - ? map.delete(names) - : names.map((name) => map.delete(name)); - this._headers.set('cookie', stringifyCookies(map)); - return result; - } - ).mockName('cookies().delete'); - - /** - * Delete all the cookies in the cookies in the request. - */ - clear = fn((): this => { - this.delete(Array.from(this._parsed.keys())); - return this; - }).mockName('cookies().clear'); - - /** - * Format the cookies in the request as a string for logging - */ - [Symbol.for('edge-runtime.inspect.custom')]() { - return `RequestCookies ${JSON.stringify(Object.fromEntries(this._parsed))}`; - } - - toString() { - return [...this._parsed.values()] - .map((v) => `${v.name}=${encodeURIComponent(v.value)}`) - .join('; '); - } + delete = fn(super.delete).mockName('next/headers::cookies().delete'); } let requestCookiesMock: RequestCookiesMock; @@ -120,7 +26,7 @@ export const cookies = fn(() => { requestCookiesMock = new RequestCookiesMock(headers()); } return requestCookiesMock; -}); +}).mockName('next/headers::cookies()'); const originalRestore = cookies.mockRestore.bind(null); diff --git a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts index a7511064fa1..02703265e58 100644 --- a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts +++ b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts @@ -1,101 +1,29 @@ import { fn } from '@storybook/test'; -import type { IncomingHttpHeaders } from 'http'; -import type { HeadersAdapter } from 'next/dist/server/web/spec-extension/adapters/headers'; -// Mostly copied from https://github.com/vercel/next.js/blob/763b9a660433ec5278a10e59d7ae89d4010ba212/packages/next/src/server/web/spec-extension/adapters/headers.ts#L20 -// @ts-expect-error unfortunately the headers property is private (and not protected) in HeadersAdapter -// and we can't access it so we need to redefine it, but that clashes with the type, hence the ts-expect-error comment. -class HeadersAdapterMock extends Headers implements HeadersAdapter { - private headers: IncomingHttpHeaders = {}; +import { HeadersAdapter } from 'next/dist/server/web/spec-extension/adapters/headers'; - /** - * Merges a header value into a string. This stores multiple values as an - * array, so we need to merge them into a string. - * - * @param value a header value - * @returns a merged header value (a string) - */ - private merge(value: string | string[]): string { - if (Array.isArray(value)) return value.join(', '); - - return value; +class HeadersAdapterMock extends HeadersAdapter { + constructor() { + super({}); } - public append = fn((name: string, value: string): void => { - const existing = this.headers[name]; - if (typeof existing === 'string') { - this.headers[name] = [existing, value]; - } else if (Array.isArray(existing)) { - existing.push(value); - } else { - this.headers[name] = value; - } - }).mockName('headers().append'); - - public delete = fn((name: string) => { - delete this.headers[name]; - }).mockName('headers().delete'); - - public get = fn((name: string): string | null => { - const value = this.headers[name]; - if (typeof value !== 'undefined') return this.merge(value); + append = fn(super.append).mockName('next/headers::headers().append'); - return null; - }).mockName('headers().get'); + delete = fn(super.delete).mockName('next/headers::headers().delete'); - public has = fn((name: string): boolean => { - return typeof this.headers[name] !== 'undefined'; - }).mockName('headers().has'); + get = fn(super.get).mockName('next/headers::headers().get'); - public set = fn((name: string, value: string): void => { - this.headers[name] = value; - }).mockName('headers().set'); + has = fn(super.has).mockName('next/headers::headers().has'); - public forEach = fn( - (callbackfn: (value: string, name: string, parent: Headers) => void, thisArg?: any): void => { - for (const [name, value] of this.entries()) { - callbackfn.call(thisArg, value, name, this); - } - } - ).mockName('headers().forEach'); + set = fn(super.set).mockName('next/headers::headers().set'); - public entries = fn( - function* (this: HeadersAdapterMock): IterableIterator<[string, string]> { - for (const key of Object.keys(this.headers)) { - const name = key.toLowerCase(); - // We assert here that this is a string because we got it from the - // Object.keys() call above. - const value = this.get(name) as string; + forEach = fn(super.forEach).mockName('next/headers::headers().forEach'); - yield [name, value]; - } - }.bind(this) - ).mockName('headers().entries'); + entries = fn(super.entries).mockName('next/headers::headers().entries'); - public keys = fn( - function* (this: HeadersAdapterMock): IterableIterator { - for (const key of Object.keys(this.headers)) { - const name = key.toLowerCase(); - yield name; - } - }.bind(this) - ).mockName('headers().keys'); + keys = fn(super.keys).mockName('next/headers::headers().keys'); - public values = fn( - function* (this: HeadersAdapterMock): IterableIterator { - for (const key of Object.keys(this.headers)) { - // We assert here that this is a string because we got it from the - // Object.keys() call above. - const value = this.get(key) as string; - - yield value; - } - }.bind(this) - ).mockName('headers().values'); - - public [Symbol.iterator](): IterableIterator<[string, string]> { - return this.entries(); - } + values = fn(super.values).mockName('next/headers::headers().values'); } let headersAdapterMock: HeadersAdapterMock; diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts index 47a7e5801b5..fbbeb8ced25 100644 --- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts @@ -17,14 +17,14 @@ let navigationAPI: { * @ignore * @internal * */ -const createNavigation = (overrides: any) => { +export const createNavigation = (overrides: any) => { const navigationActions = { - push: fn().mockName('useRouter().push'), - replace: fn().mockName('useRouter().replace'), - forward: fn().mockName('useRouter().forward'), - back: fn().mockName('useRouter().back'), - prefetch: fn().mockName('useRouter().prefetch'), - refresh: fn().mockName('useRouter().refresh'), + push: fn().mockName('next/navigation::useRouter().push'), + replace: fn().mockName('next/navigation::useRouter().replace'), + forward: fn().mockName('next/navigation::useRouter().forward'), + back: fn().mockName('next/navigation::useRouter().back'), + prefetch: fn().mockName('next/navigation::useRouter().prefetch'), + refresh: fn().mockName('next/navigation::useRouter().refresh'), }; if (overrides) { @@ -42,7 +42,7 @@ const createNavigation = (overrides: any) => { return navigationAPI; }; -const getRouter = () => { +export const getRouter = () => { if (!navigationAPI) { throw new NextjsRouterMocksNotAvailable({ importType: 'next/navigation', @@ -56,41 +56,34 @@ const getRouter = () => { export * from 'next/dist/client/components/navigation'; // mock utilities/overrides (as of Next v14.2.0) -const redirect = fn().mockName('redirect'); +export const redirect = fn().mockName('next/navigation::redirect'); // passthrough mocks - keep original implementation but allow for spying -const useSearchParams = fn(originalNavigation.useSearchParams).mockName('useSearchParams'); -const usePathname = fn(originalNavigation.usePathname).mockName('usePathname'); -const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName( +export const useSearchParams = fn(originalNavigation.useSearchParams).mockName( + 'next/navigation::useSearchParams' +); +export const usePathname = fn(originalNavigation.usePathname).mockName( + 'next/navigation::usePathname' +); +export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName( 'useSelectedLayoutSegment' ); -const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName( +export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName( 'useSelectedLayoutSegments' ); -const useRouter = fn(originalNavigation.useRouter).mockName('useRouter'); -const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName( +export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter'); +export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName( 'useServerInsertedHTML' ); -const notFound = fn(originalNavigation.notFound).mockName('notFound'); -const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName('permanentRedirect'); +export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound'); +export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName( + 'permanentRedirect' +); // Params, not exported by Next.js, is manually declared to avoid inference issues. interface Params { [key: string]: string | string[]; } -const useParams = fn<[], Params>(originalNavigation.useParams).mockName('useParams'); - -export { - createNavigation, - getRouter, - redirect, - useSearchParams, - usePathname, - useSelectedLayoutSegment, - useSelectedLayoutSegments, - useParams, - useRouter, - useServerInsertedHTML, - notFound, - permanentRedirect, -}; +export const useParams = fn<[], Params>(originalNavigation.useParams).mockName( + 'next/navigation::useParams' +); diff --git a/code/frameworks/nextjs/src/export-mocks/router/index.ts b/code/frameworks/nextjs/src/export-mocks/router/index.ts index c9f81f3a72b..b3c5e37faa4 100644 --- a/code/frameworks/nextjs/src/export-mocks/router/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/router/index.ts @@ -36,27 +36,27 @@ let routerAPI: { * @ignore * @internal * */ -const createRouter = (overrides: Partial) => { +export const createRouter = (overrides: Partial) => { const routerActions: Partial = { push: fn((..._args: any[]) => { return Promise.resolve(true); - }).mockName('useRouter().push'), + }).mockName('next/router::useRouter().push'), replace: fn((..._args: any[]) => { return Promise.resolve(true); - }).mockName('useRouter().replace'), - reload: fn((..._args: any[]) => {}).mockName('useRouter().reload'), - back: fn((..._args: any[]) => {}).mockName('useRouter().back'), - forward: fn(() => {}).mockName('useRouter().forward'), + }).mockName('next/router::useRouter().replace'), + reload: fn((..._args: any[]) => {}).mockName('next/router::useRouter().reload'), + back: fn((..._args: any[]) => {}).mockName('next/router::useRouter().back'), + forward: fn(() => {}).mockName('next/router::useRouter().forward'), prefetch: fn((..._args: any[]) => { return Promise.resolve(); - }).mockName('useRouter().prefetch'), - beforePopState: fn((..._args: any[]) => {}).mockName('useRouter().beforePopState'), + }).mockName('next/router::useRouter().prefetch'), + beforePopState: fn((..._args: any[]) => {}).mockName('next/router::useRouter().beforePopState'), }; const routerEvents: NextRouter['events'] = { - on: fn((..._args: any[]) => {}).mockName('useRouter().events.on'), - off: fn((..._args: any[]) => {}).mockName('useRouter().events.off'), - emit: fn((..._args: any[]) => {}).mockName('useRouter().events.emit'), + on: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.on'), + off: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.off'), + emit: fn((..._args: any[]) => {}).mockName('next/router::useRouter().events.emit'), }; if (overrides) { @@ -95,7 +95,7 @@ const createRouter = (overrides: Partial) => { return routerAPI as unknown as NextRouter; }; -const getRouter = () => { +export const getRouter = () => { if (!routerAPI) { throw new NextjsRouterMocksNotAvailable({ importType: 'next/router', @@ -111,7 +111,5 @@ export default singletonRouter; // mock utilities/overrides (as of Next v14.2.0) // passthrough mocks - keep original implementation but allow for spying -const useRouter = fn(originalRouter.useRouter).mockName('useRouter'); -const withRouter = fn(originalRouter.withRouter).mockName('withRouter'); - -export { createRouter, getRouter, useRouter, withRouter }; +export const useRouter = fn(originalRouter.useRouter).mockName('next/router::useRouter'); +export const withRouter = fn(originalRouter.withRouter).mockName('next/router::withRouter'); From 310aaf5aaffd0cdc14d2afe86340bb6f44aefbd2 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 12:11:13 +0200 Subject: [PATCH 02/11] Bind this --- .../nextjs/src/export-mocks/headers/cookies.ts | 10 +++++----- .../nextjs/src/export-mocks/headers/headers.ts | 18 +++++++++--------- .../nextjs/yarn.lock | 8 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts index c3272dc0e33..3fc9fdc1ae5 100644 --- a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts +++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts @@ -8,15 +8,15 @@ import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'; import { headers } from '@storybook/nextjs/headers.mock'; class RequestCookiesMock extends RequestCookies { - get = fn(super.get).mockName('next/headers::get'); + get = fn(super.get.bind(this)).mockName('next/headers::get'); - getAll = fn(super.getAll).mockName('next/headers::cookies().getAll'); + getAll = fn(super.getAll.bind(this)).mockName('next/headers::cookies().getAll'); - has = fn(super.has).mockName('next/headers::cookies().has'); + has = fn(super.has.bind(this)).mockName('next/headers::cookies().has'); - set = fn(super.set).mockName('next/headers::cookies().set'); + set = fn(super.set.bind(this)).mockName('next/headers::cookies().set'); - delete = fn(super.delete).mockName('next/headers::cookies().delete'); + delete = fn(super.delete.bind(this)).mockName('next/headers::cookies().delete'); } let requestCookiesMock: RequestCookiesMock; diff --git a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts index 02703265e58..d9eb5177b44 100644 --- a/code/frameworks/nextjs/src/export-mocks/headers/headers.ts +++ b/code/frameworks/nextjs/src/export-mocks/headers/headers.ts @@ -7,23 +7,23 @@ class HeadersAdapterMock extends HeadersAdapter { super({}); } - append = fn(super.append).mockName('next/headers::headers().append'); + append = fn(super.append.bind(this)).mockName('next/headers::headers().append'); - delete = fn(super.delete).mockName('next/headers::headers().delete'); + delete = fn(super.delete.bind(this)).mockName('next/headers::headers().delete'); - get = fn(super.get).mockName('next/headers::headers().get'); + get = fn(super.get.bind(this)).mockName('next/headers::headers().get'); - has = fn(super.has).mockName('next/headers::headers().has'); + has = fn(super.has.bind(this)).mockName('next/headers::headers().has'); - set = fn(super.set).mockName('next/headers::headers().set'); + set = fn(super.set.bind(this)).mockName('next/headers::headers().set'); - forEach = fn(super.forEach).mockName('next/headers::headers().forEach'); + forEach = fn(super.forEach.bind(this)).mockName('next/headers::headers().forEach'); - entries = fn(super.entries).mockName('next/headers::headers().entries'); + entries = fn(super.entries.bind(this)).mockName('next/headers::headers().entries'); - keys = fn(super.keys).mockName('next/headers::headers().keys'); + keys = fn(super.keys.bind(this)).mockName('next/headers::headers().keys'); - values = fn(super.values).mockName('next/headers::headers().values'); + values = fn(super.values.bind(this)).mockName('next/headers::headers().values'); } let headersAdapterMock: HeadersAdapterMock; diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock index e0efcfd9d6d..71f8b86f980 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/yarn.lock @@ -3167,7 +3167,7 @@ __metadata: "@storybook/nextjs@file:../../../code/frameworks/nextjs::locator=portable-stories-nextjs%40workspace%3A.": version: 8.1.0-alpha.7 - resolution: "@storybook/nextjs@file:../../../code/frameworks/nextjs#../../../code/frameworks/nextjs::hash=246816&locator=portable-stories-nextjs%40workspace%3A." + resolution: "@storybook/nextjs@file:../../../code/frameworks/nextjs#../../../code/frameworks/nextjs::hash=28a407&locator=portable-stories-nextjs%40workspace%3A." dependencies: "@babel/core": "npm:^7.23.2" "@babel/plugin-syntax-bigint": "npm:^7.8.3" @@ -3227,7 +3227,7 @@ __metadata: optional: true webpack: optional: true - checksum: 10/71732cc220e381872106dab3824883f4a77dcbb60901c939f58599bf92f156564a34fe646ce1159c307702be03cf532fbffaa1a515e271a6623f340d19000283 + checksum: 10/fd9a469074e0b7aba065a0b0f81f272e75e708370362ff047a653d9a19c5b753fcfab8b2f30b3f9ae226a12739b7b11054358afe4707e8d7185b9dca11c75a7c languageName: node linkType: hard @@ -3326,7 +3326,7 @@ __metadata: "@storybook/react@file:../../../code/renderers/react::locator=portable-stories-nextjs%40workspace%3A.": version: 8.1.0-alpha.7 - resolution: "@storybook/react@file:../../../code/renderers/react#../../../code/renderers/react::hash=cd73fa&locator=portable-stories-nextjs%40workspace%3A." + resolution: "@storybook/react@file:../../../code/renderers/react#../../../code/renderers/react::hash=faf3f4&locator=portable-stories-nextjs%40workspace%3A." dependencies: "@storybook/client-logger": "workspace:*" "@storybook/docs-tools": "workspace:*" @@ -3356,7 +3356,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/834084c0092a8b179f7048c1111308b6a2381cf3ee119372ed2940833b02e13577b5bc334c3f14c3224bd2d8b786939a74fbaf4dea6e8423ff610b34047412b4 + checksum: 10/f89fb42e87e17377af48b57b337dd4708053ae83d8558a0ae35af479b1faa6d7206b07934e18113707febed5ebcf94510caa89b8c92bc87cf7b5391296bbfef2 languageName: node linkType: hard From 0b243bd26d08c2b8f5aa32df503403d1baf93563 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 13:02:34 +0200 Subject: [PATCH 03/11] Update snapshot --- .../nextjs/stories/__snapshots__/portable-stories.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap index 2b8af2b9cd5..a22fa06a8ab 100644 --- a/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap +++ b/test-storybooks/portable-stories-kitchen-sink/nextjs/stories/__snapshots__/portable-stories.test.tsx.snap @@ -794,7 +794,7 @@ exports[`renders nextHeaderStories stories renders Default 1`] = ` - firstName=Jane; lastName=Doe + firstName=Jane; ; lastName=Doe;

Date: Tue, 16 Apr 2024 13:11:55 +0200 Subject: [PATCH 04/11] Fix name --- code/frameworks/nextjs/src/export-mocks/headers/cookies.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts index 3fc9fdc1ae5..3d84ecba388 100644 --- a/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts +++ b/code/frameworks/nextjs/src/export-mocks/headers/cookies.ts @@ -8,7 +8,7 @@ import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'; import { headers } from '@storybook/nextjs/headers.mock'; class RequestCookiesMock extends RequestCookies { - get = fn(super.get.bind(this)).mockName('next/headers::get'); + get = fn(super.get.bind(this)).mockName('next/headers::cookies().get'); getAll = fn(super.getAll.bind(this)).mockName('next/headers::cookies().getAll'); From b51db447ed09e16d19cc734787552680eb42b343 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 14:08:31 +0200 Subject: [PATCH 05/11] Hide some junk of next for nwo --- code/addons/actions/src/loaders.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts index d49a048da23..a1fff870b7b 100644 --- a/code/addons/actions/src/loaders.ts +++ b/code/addons/actions/src/loaders.ts @@ -18,7 +18,19 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => { typeof global.__STORYBOOK_TEST_ON_MOCK_CALL__ === 'function' ) { const onMockCall = global.__STORYBOOK_TEST_ON_MOCK_CALL__ as typeof onMockCallType; - onMockCall((mock, args) => action(mock.getMockName())(args)); + onMockCall((mock, args) => { + const name = mock.getMockName(); + + // TODO: Make this a configurable API in 8.2 + if ( + !/^next\/.*::/.test(name) || + ((name.startsWith('next/headers::cookies()') || + name.startsWith('next/headers::headers()')) && + (name.endsWith('set') || name.endsWith('delete'))) + ) { + action(name)(args); + } + }); subscribed = true; } }; From a1d631b8748f7d08b44f97a67c0953aef9f1af3a Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 14:13:47 +0200 Subject: [PATCH 06/11] Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts Co-authored-by: Yann Braga --- code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts index fbbeb8ced25..61344102c24 100644 --- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts @@ -77,7 +77,7 @@ export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML ); export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound'); export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName( - 'permanentRedirect' + 'next/navigation::permanentRedirect' ); // Params, not exported by Next.js, is manually declared to avoid inference issues. From 2beb7ba087f771ee85d1126d5cc025359853b2d3 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 14:13:53 +0200 Subject: [PATCH 07/11] Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts Co-authored-by: Yann Braga --- code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts index 61344102c24..2973c65120e 100644 --- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts @@ -66,7 +66,7 @@ export const usePathname = fn(originalNavigation.usePathname).mockName( 'next/navigation::usePathname' ); export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutSegment).mockName( - 'useSelectedLayoutSegment' + 'next/navigation::useSelectedLayoutSegment' ); export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName( 'useSelectedLayoutSegments' From d83691a92415159962ce34fef32ebb6fef3d6cba Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 14:14:02 +0200 Subject: [PATCH 08/11] Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts Co-authored-by: Yann Braga --- code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts index 2973c65120e..63458b0fc92 100644 --- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts @@ -73,7 +73,7 @@ export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayout ); export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter'); export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName( - 'useServerInsertedHTML' + 'next/navigation::useServerInsertedHTML' ); export const notFound = fn(originalNavigation.notFound).mockName('next/navigation::notFound'); export const permanentRedirect = fn(originalNavigation.permanentRedirect).mockName( From 1aa833d1a3cd079433f004d8f3f464b2579eb3ab Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 14:14:07 +0200 Subject: [PATCH 09/11] Update code/frameworks/nextjs/src/export-mocks/navigation/index.ts Co-authored-by: Yann Braga --- code/frameworks/nextjs/src/export-mocks/navigation/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts index 63458b0fc92..dd9e9a692e6 100644 --- a/code/frameworks/nextjs/src/export-mocks/navigation/index.ts +++ b/code/frameworks/nextjs/src/export-mocks/navigation/index.ts @@ -69,7 +69,7 @@ export const useSelectedLayoutSegment = fn(originalNavigation.useSelectedLayoutS 'next/navigation::useSelectedLayoutSegment' ); export const useSelectedLayoutSegments = fn(originalNavigation.useSelectedLayoutSegments).mockName( - 'useSelectedLayoutSegments' + 'next/navigation::useSelectedLayoutSegments' ); export const useRouter = fn(originalNavigation.useRouter).mockName('next/navigation::useRouter'); export const useServerInsertedHTML = fn(originalNavigation.useServerInsertedHTML).mockName( From 8b6bbbfefcd640434944a2d478e258ad6b54a1cd Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 14:19:52 +0200 Subject: [PATCH 10/11] Hide some junk of next for now --- code/addons/actions/src/loaders.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts index a1fff870b7b..7f279bba577 100644 --- a/code/addons/actions/src/loaders.ts +++ b/code/addons/actions/src/loaders.ts @@ -24,6 +24,8 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => { // TODO: Make this a configurable API in 8.2 if ( !/^next\/.*::/.test(name) || + name.startsWith('next/router::useRouter()') || + name.startsWith('next/navigation::useRouter()') || ((name.startsWith('next/headers::cookies()') || name.startsWith('next/headers::headers()')) && (name.endsWith('set') || name.endsWith('delete'))) From 8dbd04c4c73b6a55fc9e77b129435fc8fde0e7be Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 16 Apr 2024 15:09:15 +0200 Subject: [PATCH 11/11] Show redirect and next/cache spies --- code/addons/actions/src/loaders.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts index 7f279bba577..eebb09acb71 100644 --- a/code/addons/actions/src/loaders.ts +++ b/code/addons/actions/src/loaders.ts @@ -24,11 +24,16 @@ const logActionsWhenMockCalled: LoaderFunction = (context) => { // TODO: Make this a configurable API in 8.2 if ( !/^next\/.*::/.test(name) || - name.startsWith('next/router::useRouter()') || - name.startsWith('next/navigation::useRouter()') || - ((name.startsWith('next/headers::cookies()') || - name.startsWith('next/headers::headers()')) && - (name.endsWith('set') || name.endsWith('delete'))) + [ + 'next/router::useRouter()', + 'next/navigation::useRouter()', + 'next/navigation::redirect', + 'next/cache::', + 'next/headers::cookies().set', + 'next/headers::cookies().delete', + 'next/headers::headers().set', + 'next/headers::headers().delete', + ].some((prefix) => name.startsWith(prefix)) ) { action(name)(args); }