diff --git a/packages/api-server/src/plugins/graphql.ts b/packages/api-server/src/plugins/graphql.ts index 86bdb7980eba..0d97050a3ecc 100644 --- a/packages/api-server/src/plugins/graphql.ts +++ b/packages/api-server/src/plugins/graphql.ts @@ -115,7 +115,7 @@ export async function redwoodFastifyGraphQLServer( }) } - fastify.ready(() => { + fastify.addHook('onReady', (done) => { console.info(`GraphQL Yoga Server endpoint at ${yoga.graphqlEndpoint}`) console.info( `GraphQL Yoga Server Health Check endpoint at ${yoga.graphqlEndpoint}/health` @@ -123,6 +123,8 @@ export async function redwoodFastifyGraphQLServer( console.info( `GraphQL Yoga Server Readiness endpoint at ${yoga.graphqlEndpoint}/readiness` ) + + done() }) done() diff --git a/packages/api/src/__tests__/normalizeRequest.test.ts b/packages/api/src/__tests__/normalizeRequest.test.ts index 5be836c166ac..c39e316eceda 100644 --- a/packages/api/src/__tests__/normalizeRequest.test.ts +++ b/packages/api/src/__tests__/normalizeRequest.test.ts @@ -1,5 +1,6 @@ import { Headers } from '@whatwg-node/fetch' import type { APIGatewayProxyEvent } from 'aws-lambda' +import { test, expect, describe } from 'vitest' import { normalizeRequest } from '../transforms' @@ -54,7 +55,7 @@ export const createMockedLambdaEvent = ( } describe('Lambda Request', () => { - it('Normalizes an aws event with base64', async () => { + test('Normalizes an aws event with base64', async () => { const corsEventB64 = createMockedLambdaEvent( 'POST', Buffer.from(JSON.stringify({ bazinga: 'hello_world' }), 'utf8').toString( @@ -73,14 +74,14 @@ describe('Lambda Request', () => { }) }) - it('Handles CORS requests with and without b64 encoded', async () => { + test('Handles CORS requests with and without b64 encoded', async () => { const corsEventB64 = createMockedLambdaEvent('OPTIONS', undefined, true) expect(await normalizeRequest(corsEventB64)).toEqual({ headers: new Headers(corsEventB64.headers as Record), // headers returned as symbol method: 'OPTIONS', query: null, - jsonBody: undefined, + jsonBody: {}, }) const corsEventWithoutB64 = createMockedLambdaEvent( @@ -93,13 +94,13 @@ describe('Lambda Request', () => { headers: new Headers(corsEventB64.headers as Record), // headers returned as symbol method: 'OPTIONS', query: null, - jsonBody: undefined, + jsonBody: {}, }) }) }) describe('Fetch API Request', () => { - it('Normalizes a fetch event', async () => { + test('Normalizes a fetch event', async () => { const fetchEvent = new Request( 'http://localhost:9210/graphql?whatsup=doc&its=bugs', { @@ -128,7 +129,7 @@ describe('Fetch API Request', () => { expect(partial.headers.get('content-type')).toEqual('application/json') }) - it('Handles an empty body', async () => { + test('Handles an empty body', async () => { const headers = { 'content-type': 'application/json', 'x-custom-header': 'bazinga', @@ -151,7 +152,7 @@ describe('Fetch API Request', () => { whatsup: 'doc', its: 'bugs', }, - jsonBody: undefined, + jsonBody: {}, // @NOTE empty body is {} not undefined }) expect(partial.headers.get('content-type')).toEqual(headers['content-type']) diff --git a/packages/api/src/auth/index.ts b/packages/api/src/auth/index.ts index 7f1706675926..90bd84c54b86 100644 --- a/packages/api/src/auth/index.ts +++ b/packages/api/src/auth/index.ts @@ -5,6 +5,8 @@ import { parse as parseCookie } from 'cookie' import { getEventHeader } from '../event' +import { getEventHeader } from '../event' + import type { Decoded } from './parseJWT' export type { Decoded } @@ -64,16 +66,24 @@ export const parseAuthorizationHeader = ( return { schema, token } } +/** @MARK Note that we do not send LambdaContext when making fetch requests + * + * This part is incomplete, as we need to decide how we will make the breaking change to + * 1. getCurrentUser + * 2. authDecoders + + */ + export type AuthContextPayload = [ Decoded, { type: string } & AuthorizationHeader, - { event: APIGatewayProxyEvent; context: LambdaContext } + { event: APIGatewayProxyEvent | Request; context: LambdaContext } ] export type Decoder = ( token: string, type: string, - req: { event: APIGatewayProxyEvent; context: LambdaContext } + req: { event: APIGatewayProxyEvent | Request; context: LambdaContext } ) => Promise /** @@ -134,8 +144,9 @@ export const getAuthenticationContext = async ({ let i = 0 while (!decoded && i < authDecoders.length) { decoded = await authDecoders[i](token, type, { - // @TODO We will need to make a breaking change to auth decoders maybe - event: event as any, + // @TODO: We will need to make a breaking change to support `Request` objects. + // We can remove this typecast + event: event, context, }) i++ diff --git a/packages/api/src/transforms.ts b/packages/api/src/transforms.ts index 4a457e8d2f67..3fb7eb010f86 100644 --- a/packages/api/src/transforms.ts +++ b/packages/api/src/transforms.ts @@ -5,7 +5,7 @@ import type { APIGatewayProxyEvent } from 'aws-lambda' // We do this to keep the API consistent between the two // When we support only the FetchAPI request, we should remove this export interface PartialRequest> { - jsonBody?: TBody + jsonBody: TBody headers: Headers method: string query: any @@ -16,7 +16,7 @@ export interface PartialRequest> { */ export const parseLambdaEventBody = (event: APIGatewayProxyEvent) => { if (!event.body) { - return + return {} } if (event.isBase64Encoded) { @@ -28,23 +28,19 @@ export const parseLambdaEventBody = (event: APIGatewayProxyEvent) => { /** * Extracts and parses body payload from Fetch Request - * with check for empty body (and base64 later) + * with check for empty body + * + * NOTE: whatwg/server expects that you will decode the base64 body yourself + * see readme here: https://github.com/ardatan/whatwg-node/tree/master/packages/server#aws-lambda */ export const parseFetchEventBody = async (event: Request) => { if (!event.body) { - return + return {} } - // @TODO: We need to understand what happens on Vercel - // if (event.isBase64Encoded) { - // return JSON.parse(Buffer.from(event.body, 'base64').toString('utf-8')) - // } else { - // return JSON.parse(event.body) - // } - const body = await event.text() - return body ? JSON.parse(body) : undefined + return body ? JSON.parse(body) : {} } export const isFetchApiRequest = ( diff --git a/packages/auth-providers/auth0/api/jest.config.js b/packages/auth-providers/auth0/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/auth0/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/auth0/api/package.json b/packages/auth-providers/auth0/api/package.json index 01aad3357389..640d627f0c66 100644 --- a/packages/auth-providers/auth0/api/package.json +++ b/packages/auth-providers/auth0/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -33,8 +33,8 @@ "@babel/core": "^7.22.20", "@redwoodjs/api": "6.0.7", "@types/jsonwebtoken": "9.0.5", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/auth0/api/src/__tests__/auth0.test.ts b/packages/auth-providers/auth0/api/src/__tests__/auth0.test.ts index 5bc90f1f303f..8ca10945be3f 100644 --- a/packages/auth-providers/auth0/api/src/__tests__/auth0.test.ts +++ b/packages/auth-providers/auth0/api/src/__tests__/auth0.test.ts @@ -1,10 +1,13 @@ import jwt from 'jsonwebtoken' +import { vi, test, expect } from 'vitest' import { verifyAuth0Token } from '../decoder' -jest.mock('jsonwebtoken', () => ({ - verify: jest.fn(), - decode: jest.fn(), +vi.mock('jsonwebtoken', () => ({ + default: { + verify: vi.fn(), + decode: vi.fn(), + }, })) test('verify, and not decode, should be called in production', () => { diff --git a/packages/auth-providers/auth0/api/vitest.config.mts b/packages/auth-providers/auth0/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/auth0/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/auth0/setup/jest.config.js b/packages/auth-providers/auth0/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/auth0/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/auth0/setup/package.json b/packages/auth-providers/auth0/setup/package.json index 9902868d89ca..609f1b631cad 100644 --- a/packages/auth-providers/auth0/setup/package.json +++ b/packages/auth-providers/auth0/setup/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,8 +31,8 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/auth0/setup/src/__tests__/setup.test.ts b/packages/auth-providers/auth0/setup/src/__tests__/setup.test.ts index 9d1abf7d4e00..ea77ef05fa0a 100644 --- a/packages/auth-providers/auth0/setup/src/__tests__/setup.test.ts +++ b/packages/auth-providers/auth0/setup/src/__tests__/setup.test.ts @@ -1,7 +1,9 @@ +import { vi, test, expect } from 'vitest' + import { command, description, builder, handler } from '../setup' // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { errorTelemetry: () => jest.fn(), timedTelemetry: () => jest.fn(), diff --git a/packages/auth-providers/auth0/setup/vitest.config.mts b/packages/auth-providers/auth0/setup/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/auth0/setup/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/auth0/web/jest.config.js b/packages/auth-providers/auth0/web/jest.config.js deleted file mode 100644 index e691bb8f6dbd..000000000000 --- a/packages/auth-providers/auth0/web/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/auth0/web/package.json b/packages/auth-providers/auth0/web/package.json index 834e79ded6e2..931a159e2820 100644 --- a/packages/auth-providers/auth0/web/package.json +++ b/packages/auth-providers/auth0/web/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -32,9 +32,9 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/react": "18.2.37", - "jest": "29.7.0", "react": "0.0.0-experimental-e5205658f-20230913", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "@auth0/auth0-spa-js": "2.1.2" diff --git a/packages/auth-providers/auth0/web/src/__tests__/auth0.test.tsx b/packages/auth-providers/auth0/web/src/__tests__/auth0.test.tsx index d939ea483e70..62f821cafbab 100644 --- a/packages/auth-providers/auth0/web/src/__tests__/auth0.test.tsx +++ b/packages/auth-providers/auth0/web/src/__tests__/auth0.test.tsx @@ -5,6 +5,7 @@ import type { User, } from '@auth0/auth0-spa-js' import { renderHook, act } from '@testing-library/react' +import { vi, beforeAll, beforeEach, describe, expect, it } from 'vitest' import type { CurrentUser } from '@redwoodjs/auth' @@ -52,7 +53,7 @@ const auth0MockClient: Partial = { }, } -const fetchMock = jest.fn() +const fetchMock = vi.fn() fetchMock.mockImplementation(async (_url, options) => { const body = options?.body ? JSON.parse(options.body) : {} diff --git a/packages/auth-providers/auth0/web/vitest.config.mts b/packages/auth-providers/auth0/web/vitest.config.mts new file mode 100644 index 000000000000..52ec6b09db8f --- /dev/null +++ b/packages/auth-providers/auth0/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + environment: 'jsdom', + }, +}) diff --git a/packages/auth-providers/azureActiveDirectory/api/jest.config.js b/packages/auth-providers/azureActiveDirectory/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/azureActiveDirectory/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/azureActiveDirectory/api/package.json b/packages/auth-providers/azureActiveDirectory/api/package.json index ec16c01ba49f..2164e6e249dc 100644 --- a/packages/auth-providers/azureActiveDirectory/api/package.json +++ b/packages/auth-providers/azureActiveDirectory/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -34,8 +34,8 @@ "@redwoodjs/api": "6.0.7", "@types/aws-lambda": "8.10.126", "@types/jsonwebtoken": "9.0.5", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/azureActiveDirectory/api/src/__tests__/azureActiveDirectory.test.ts b/packages/auth-providers/azureActiveDirectory/api/src/__tests__/azureActiveDirectory.test.ts index 1afa1ecd1b4b..f58e3c9ee3e9 100644 --- a/packages/auth-providers/azureActiveDirectory/api/src/__tests__/azureActiveDirectory.test.ts +++ b/packages/auth-providers/azureActiveDirectory/api/src/__tests__/azureActiveDirectory.test.ts @@ -1,11 +1,14 @@ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda' import jwt from 'jsonwebtoken' +import { vi, test, beforeAll, afterAll, expect, describe } from 'vitest' import { authDecoder } from '../decoder' -jest.mock('jsonwebtoken', () => ({ - verify: jest.fn(), - decode: jest.fn(), +vi.mock('jsonwebtoken', () => ({ + default: { + verify: vi.fn(), + decode: vi.fn(), + }, })) const req = { diff --git a/packages/auth-providers/azureActiveDirectory/api/vitest.config.mts b/packages/auth-providers/azureActiveDirectory/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/azureActiveDirectory/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/azureActiveDirectory/setup/jest.config.js b/packages/auth-providers/azureActiveDirectory/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/azureActiveDirectory/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/azureActiveDirectory/setup/package.json b/packages/auth-providers/azureActiveDirectory/setup/package.json index b642e6ab6ea1..19d3f655439b 100644 --- a/packages/auth-providers/azureActiveDirectory/setup/package.json +++ b/packages/auth-providers/azureActiveDirectory/setup/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,8 +31,8 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/azureActiveDirectory/setup/src/__tests__/setup.test.ts b/packages/auth-providers/azureActiveDirectory/setup/src/__tests__/setup.test.ts index 15b0eecace52..3f3e336466aa 100644 --- a/packages/auth-providers/azureActiveDirectory/setup/src/__tests__/setup.test.ts +++ b/packages/auth-providers/azureActiveDirectory/setup/src/__tests__/setup.test.ts @@ -1,10 +1,12 @@ +import { vi, test, expect } from 'vitest' + import { command, description, builder, handler } from '../setup' // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { - errorTelemetry: () => jest.fn(), - timedTelemetry: () => jest.fn(), + errorTelemetry: () => vi.fn(), + timedTelemetry: () => vi.fn(), } }) diff --git a/packages/auth-providers/azureActiveDirectory/setup/vitest.config.mts b/packages/auth-providers/azureActiveDirectory/setup/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/azureActiveDirectory/setup/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/azureActiveDirectory/web/jest.config.js b/packages/auth-providers/azureActiveDirectory/web/jest.config.js deleted file mode 100644 index e691bb8f6dbd..000000000000 --- a/packages/auth-providers/azureActiveDirectory/web/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/azureActiveDirectory/web/package.json b/packages/auth-providers/azureActiveDirectory/web/package.json index 6fe008d43522..c3c33a9612dc 100644 --- a/packages/auth-providers/azureActiveDirectory/web/package.json +++ b/packages/auth-providers/azureActiveDirectory/web/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -33,9 +33,9 @@ "@babel/core": "^7.22.20", "@types/netlify-identity-widget": "1.9.6", "@types/react": "18.2.37", - "jest": "29.7.0", "react": "0.0.0-experimental-e5205658f-20230913", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "@azure/msal-browser": "2.38.3" diff --git a/packages/auth-providers/azureActiveDirectory/web/src/__tests__/azureActiveDirectory.test.tsx b/packages/auth-providers/azureActiveDirectory/web/src/__tests__/azureActiveDirectory.test.tsx index f34c4103bcd1..ef8032bfa58d 100644 --- a/packages/auth-providers/azureActiveDirectory/web/src/__tests__/azureActiveDirectory.test.tsx +++ b/packages/auth-providers/azureActiveDirectory/web/src/__tests__/azureActiveDirectory.test.tsx @@ -4,6 +4,7 @@ import type { RedirectRequest, } from '@azure/msal-browser' import { renderHook, act } from '@testing-library/react' +import { vi, it, expect, describe, beforeAll, beforeEach } from 'vitest' import type { CurrentUser } from '@redwoodjs/auth' @@ -79,7 +80,7 @@ const azureActiveDirectoryMockClient: Partial = { }, } -const fetchMock = jest.fn() +const fetchMock = vi.fn() fetchMock.mockImplementation(async (_url, options) => { const body = options?.body ? JSON.parse(options.body) : {} diff --git a/packages/auth-providers/azureActiveDirectory/web/vitest.config.mts b/packages/auth-providers/azureActiveDirectory/web/vitest.config.mts new file mode 100644 index 000000000000..52ec6b09db8f --- /dev/null +++ b/packages/auth-providers/azureActiveDirectory/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + environment: 'jsdom', + }, +}) diff --git a/packages/auth-providers/clerk/api/jest.config.js b/packages/auth-providers/clerk/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/clerk/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/clerk/api/package.json b/packages/auth-providers/clerk/api/package.json index 1cd8f651f5e4..8d92f5421b2b 100644 --- a/packages/auth-providers/clerk/api/package.json +++ b/packages/auth-providers/clerk/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -32,8 +32,8 @@ "@babel/core": "^7.22.20", "@redwoodjs/api": "6.0.7", "@types/aws-lambda": "8.10.126", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/clerk/api/src/__tests__/clerk.test.ts b/packages/auth-providers/clerk/api/src/__tests__/clerk.test.ts index d80fe627d223..1ccea2ef70a4 100644 --- a/packages/auth-providers/clerk/api/src/__tests__/clerk.test.ts +++ b/packages/auth-providers/clerk/api/src/__tests__/clerk.test.ts @@ -1,4 +1,5 @@ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda' +import { beforeAll, afterAll, describe, test, expect } from 'vitest' import { authDecoder, clerkAuthDecoder } from '../decoder' diff --git a/packages/auth-providers/clerk/api/vitest.config.mts b/packages/auth-providers/clerk/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/clerk/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/clerk/setup/jest.config.js b/packages/auth-providers/clerk/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/clerk/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/clerk/setup/package.json b/packages/auth-providers/clerk/setup/package.json index b6747fe513eb..8336bede7098 100644 --- a/packages/auth-providers/clerk/setup/package.json +++ b/packages/auth-providers/clerk/setup/package.json @@ -18,9 +18,7 @@ "build:pack": "yarn pack -o redwoodjs-auth-clerk-setup.tgz", "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", - "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src --passWithNoTests", - "test:watch": "yarn test --watch" + "prepublishOnly": "NODE_ENV=production yarn build" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,7 +29,6 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", "typescript": "5.3.3" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" diff --git a/packages/auth-providers/clerk/web/jest.config.js b/packages/auth-providers/clerk/web/jest.config.js deleted file mode 100644 index e691bb8f6dbd..000000000000 --- a/packages/auth-providers/clerk/web/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/clerk/web/package.json b/packages/auth-providers/clerk/web/package.json index bdf2ae2e5c88..19f29506bcd6 100644 --- a/packages/auth-providers/clerk/web/package.json +++ b/packages/auth-providers/clerk/web/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -33,9 +33,9 @@ "@clerk/clerk-react": "4.28.3", "@clerk/types": "3.60.0", "@types/react": "18.2.37", - "jest": "29.7.0", "react": "0.0.0-experimental-e5205658f-20230913", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "@clerk/clerk-react": "4.28.3" diff --git a/packages/auth-providers/clerk/web/src/__tests__/clerk.test.tsx b/packages/auth-providers/clerk/web/src/__tests__/clerk.test.tsx index 93d6faccbabf..2ddaa1974573 100644 --- a/packages/auth-providers/clerk/web/src/__tests__/clerk.test.tsx +++ b/packages/auth-providers/clerk/web/src/__tests__/clerk.test.tsx @@ -5,6 +5,7 @@ import type { ActiveSessionResource, } from '@clerk/types' import { renderHook, act } from '@testing-library/react' +import { vi, expect, describe, it, beforeAll, beforeEach } from 'vitest' import type { CurrentUser } from '@redwoodjs/auth' @@ -63,7 +64,7 @@ const clerkMockClient: Partial = { }, } -const fetchMock = jest.fn() +const fetchMock = vi.fn() fetchMock.mockImplementation(async (_url, options) => { const body = options?.body ? JSON.parse(options.body) : {} diff --git a/packages/auth-providers/clerk/web/vitest.config.mts b/packages/auth-providers/clerk/web/vitest.config.mts new file mode 100644 index 000000000000..52ec6b09db8f --- /dev/null +++ b/packages/auth-providers/clerk/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + environment: 'jsdom', + }, +}) diff --git a/packages/auth-providers/custom/setup/jest.config.js b/packages/auth-providers/custom/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/custom/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/custom/setup/package.json b/packages/auth-providers/custom/setup/package.json index ebc3c711846a..300958d96bc6 100644 --- a/packages/auth-providers/custom/setup/package.json +++ b/packages/auth-providers/custom/setup/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,8 +31,8 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/custom/setup/src/__tests__/setup.test.ts b/packages/auth-providers/custom/setup/src/__tests__/setup.test.ts index 1383a37488b8..27eac4ac2bde 100644 --- a/packages/auth-providers/custom/setup/src/__tests__/setup.test.ts +++ b/packages/auth-providers/custom/setup/src/__tests__/setup.test.ts @@ -1,10 +1,12 @@ +import { vi, test, expect } from 'vitest' + import { command, description, builder, handler } from '../setup' // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { - errorTelemetry: () => jest.fn(), - timedTelemetry: () => jest.fn(), + errorTelemetry: () => vi.fn(), + timedTelemetry: () => vi.fn(), } }) diff --git a/packages/auth-providers/custom/setup/vitest.config.mts b/packages/auth-providers/custom/setup/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/custom/setup/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/dbAuth/api/jest.config.js b/packages/auth-providers/dbAuth/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/dbAuth/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/dbAuth/api/package.json b/packages/auth-providers/dbAuth/api/package.json index 61ed80ea4628..694be84376b3 100644 --- a/packages/auth-providers/dbAuth/api/package.json +++ b/packages/auth-providers/dbAuth/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -37,8 +37,8 @@ "@simplewebauthn/server": "7.4.0", "@types/md5": "2.3.5", "@types/uuid": "9.0.7", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts b/packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts index 532e2d239260..9ca96bca1d7f 100644 --- a/packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts +++ b/packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts @@ -539,8 +539,6 @@ export class DbAuthHandler< ) } - await this.init() - const { username } = this.normalizedRequest.jsonBody || {} // was the username sent in at all? if (!username || username.trim() === '') { @@ -608,6 +606,8 @@ export class DbAuthHandler< } } + // In SSR this never gets called... + // @TODO remove? async getToken() { try { // Just return the encrypted session cookie, to be passed back in the Authorization header @@ -631,7 +631,6 @@ export class DbAuthHandler< ) } - await this.init() const { username, password } = this.normalizedRequest.jsonBody || {} const dbUser = await this._verifyUser(username, password) const handlerUser = await (this.options.login as LoginFlowOptions).handler( @@ -661,7 +660,6 @@ export class DbAuthHandler< ) } - await this.init() const { password, resetToken } = this.normalizedRequest.jsonBody || {} // is the resetToken present? @@ -757,7 +755,6 @@ export class DbAuthHandler< } async validateResetToken() { - await this.init() const { resetToken } = this.normalizedRequest.jsonBody || {} // is token present at all? if (!resetToken || String(resetToken).trim() === '') { @@ -790,6 +787,12 @@ export class DbAuthHandler< throw new DbAuthError.WebAuthnError('Missing Id in request') } + const { rawId } = this.normalizedRequest.jsonBody || {} + + if (!rawId) { + throw new DbAuthError.WebAuthnError('Missing Id in request') + } + if (!webAuthnOptions || !webAuthnOptions.enabled) { throw new DbAuthError.WebAuthnError('WebAuthn is not enabled') } @@ -875,7 +878,6 @@ export class DbAuthHandler< if (this.options.webAuthn === undefined || !this.options.webAuthn.enabled) { throw new DbAuthError.WebAuthnError('WebAuthn is not enabled') } - await this.init() const webAuthnOptions = this.options.webAuthn @@ -1227,7 +1229,7 @@ export class DbAuthHandler< // checks the CSRF token in the header against the CSRF token in the session // and throw an error if they are not the same (not used yet) - _validateCsrf() { + async _validateCsrf() { if ( this.sessionCsrfToken !== this.normalizedRequest.headers.get('csrf-token') ) { @@ -1419,7 +1421,6 @@ export class DbAuthHandler< // creates and returns a user, first checking that the username/password // values pass validation async _createUser() { - await this.init() const { username, password, ...userAttributes } = this.normalizedRequest.jsonBody || {} if ( @@ -1457,7 +1458,6 @@ export class DbAuthHandler< // figure out which auth method we're trying to call async _getAuthMethod() { - await this.init() // try getting it from the query string, /.redwood/functions/auth?method=[methodName] let methodName = this.normalizedRequest.query.method as AuthMethodNames diff --git a/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.fetch.test.js b/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.fetch.test.js index 1976f45173c5..7f78bd11097f 100644 --- a/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.fetch.test.js +++ b/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.fetch.test.js @@ -1,6 +1,17 @@ import crypto from 'node:crypto' import path from 'node:path' +import { + vi, + describe, + it, + expect, + beforeAll, + afterAll, + beforeEach, + afterEach, +} from 'vitest' + import { DbAuthHandler } from '../DbAuthHandler' import * as dbAuthError from '../errors' import { hashToken } from '../shared' @@ -136,7 +147,7 @@ let req, context, options describe('dbAuth', () => { beforeEach(() => { // hide deprecation warnings during test - jest.spyOn(console, 'warn').mockImplementation(() => {}) + vi.spyOn(console, 'warn').mockImplementation(() => {}) // encryption key so results are consistent regardless of settings in .env process.env.SESSION_SECRET = SESSION_SECRET delete process.env.DBAUTH_COOKIE_DOMAIN @@ -221,7 +232,7 @@ describe('dbAuth', () => { }) afterEach(async () => { - jest.spyOn(console, 'warn').mockRestore() + vi.spyOn(console, 'warn').mockRestore() await db.user.deleteMany({ where: { email: 'rob@redwoodjs.com' }, }) @@ -250,22 +261,25 @@ describe('dbAuth', () => { }) describe('dbAccessor', () => { - it('returns the prisma db accessor for a model', () => { + it('returns the prisma db accessor for a model', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(dbAuth.dbAccessor).toEqual(db.user) }) }) describe('dbCredentialAccessor', () => { - it('returns the prisma db accessor for a UserCredential model', () => { + it('returns the prisma db accessor for a UserCredential model', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(dbAuth.dbCredentialAccessor).toEqual(db.userCredential) }) }) describe('sessionExpiresDate', () => { - it('returns a date in the future as a UTCString', () => { + it('returns a date in the future as a UTCString', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const expiresAt = new Date() expiresAt.setSeconds(expiresAt.getSeconds() + options.login.expires) @@ -274,8 +288,9 @@ describe('dbAuth', () => { }) describe('webAuthnExpiresDate', () => { - it('returns a date in the future as a UTCString', () => { + it('returns a date in the future as a UTCString', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const expiresAt = new Date() expiresAt.setSeconds(expiresAt.getSeconds() + options.webAuthn.expires) @@ -284,8 +299,9 @@ describe('dbAuth', () => { }) describe('_deleteSessionHeader', () => { - it('returns a Set-Cookie header to delete the session cookie', () => { + it('returns a Set-Cookie header to delete the session cookie', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const headers = dbAuth._deleteSessionHeader expect(Object.keys(headers).length).toEqual(1) @@ -295,7 +311,7 @@ describe('dbAuth', () => { }) describe('constructor', () => { - it('initializes some variables with passed values', () => { + it('initializes some variables with passed values', async () => { req = { headers: {} } context = { foo: 'bar' } options = { @@ -315,6 +331,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(dbAuth.event).toEqual(req) expect(dbAuth.options).toEqual(options) @@ -513,6 +530,7 @@ describe('dbAuth', () => { it('parses params from a plain text body', async () => { req = { headers: {}, body: `{"foo":"bar", "baz":123}` } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() // Need to wait for reqq to be parsed await dbAuth.init() @@ -523,89 +541,53 @@ describe('dbAuth', () => { }) }) - it.skip('parses an empty plain text body and still sets params', async () => { - // @TODO(Rob): This test is failing due to refactor, not sure its necessary - req = { isBase64Encoded: false, headers: {}, body: '' } - context = { foo: 'bar' } - const dbAuth = new DbAuthHandler(req, context, options) - await dbAuth.init() - - expect(dbAuth.normalizedRequest.jsonBody).toEqual({}) - }) - - it.skip('parses params from an undefined body when isBase64Encoded == false', async () => { - // @TODO(Rob): This test is failing due to refactor, not sure its necessary + it('parses an empty plain text body and still sets params', async () => { + const req = new Request('http://localhost:8910/_rw_mw', { + method: 'POST', + body: '', + }) - req = { - isBase64Encoded: false, - headers: {}, - } context = { foo: 'bar' } const dbAuth = new DbAuthHandler(req, context, options) await dbAuth.init() + await dbAuth.init() expect(dbAuth.normalizedRequest.jsonBody).toEqual({}) }) - it('parses params from a base64 encoded body', async () => { - req = { - isBase64Encoded: true, - headers: {}, - body: Buffer.from(`{"foo":"bar", "baz":123}`, 'utf8'), - } - const dbAuth = new DbAuthHandler(req, context, options) - await dbAuth.init() - expect(dbAuth.normalizedRequest.jsonBody).toEqual({ - foo: 'bar', - baz: 123, - }) - }) - - it('parses params from an undefined body when isBase64Encoded == true', async () => { - // @TODO(Rob): Not sure this is necessary any more? + it('parses params from an undefined body', async () => { req = { - isBase64Encoded: true, + isBase64Encoded: false, headers: {}, } context = { foo: 'bar' } const dbAuth = new DbAuthHandler(req, context, options) await dbAuth.init() - - expect(dbAuth.normalizedRequest.jsonBody).toEqual(undefined) - }) - - it('parses params from an empty body when isBase64Encoded == true', async () => { - // @TODO(Rob): Not sure this is necessary any more? - req = { - isBase64Encoded: true, - headers: {}, - body: '', - } - context = { foo: 'bar' } - const dbAuth = new DbAuthHandler(req, context, options) await dbAuth.init() - expect(dbAuth.normalizedRequest.jsonBody).toEqual(undefined) + expect(dbAuth.normalizedRequest.jsonBody).toEqual({}) }) it('sets header-based CSRF token', async () => { req = { headers: { 'csrf-token': 'qwerty' } } const dbAuth = new DbAuthHandler(req, context, options) await dbAuth.init() + await dbAuth.init() expect(dbAuth.normalizedRequest.headers.get('csrf-token')).toEqual( 'qwerty' ) }) - it('sets session variables to nothing if session cannot be decrypted', () => { + it('sets session variables to nothing if session cannot be decrypted', async () => { req = { headers: { 'csrf-token': 'qwerty' } } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(dbAuth.session).toBeUndefined() expect(dbAuth.sessionCsrfToken).toBeUndefined() }) - it('sets session variables to valid session data', () => { + it('sets session variables to valid session data', async () => { req = { headers: { cookie: @@ -613,6 +595,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(dbAuth.session).toEqual({ foo: 'bar' }) expect(dbAuth.sessionCsrfToken).toEqual('abcd') @@ -658,6 +641,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(fetchEvent, context, options) + await dbAuth.init() const response = await dbAuth.invoke() expect(response.statusCode).toEqual(404) @@ -673,6 +657,7 @@ describe('dbAuth', () => { body: JSON.stringify({ method: 'foobar' }), }) const dbAuth = new DbAuthHandler(fetchEvent, context, options) + const response = await dbAuth.invoke() expect(response.statusCode).toEqual(404) @@ -689,7 +674,9 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(fetchEvent, context, options) - dbAuth.logout = jest.fn(() => { + await dbAuth.init() + + dbAuth.logout = vi.fn(() => { throw Error('Logout error') }) const response = await dbAuth.invoke() @@ -711,7 +698,7 @@ describe('dbAuth', () => { credentials: true, }, }) - dbAuth.logout = jest.fn(() => { + dbAuth.logout = vi.fn(() => { throw Error('Logout error') }) const response = await dbAuth.invoke() @@ -734,7 +721,8 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(fetchEvent, context, options) - dbAuth.logout = jest.fn(() => ['body', { foo: 'bar' }]) + await dbAuth.init() + dbAuth.logout = vi.fn(() => ['body', { foo: 'bar' }]) const response = await dbAuth.invoke() expect(dbAuth.logout).toHaveBeenCalled() @@ -762,6 +750,7 @@ describe('dbAuth', () => { options.forgotPassword.enabled = false const dbAuth = new DbAuthHandler(fetchEvent, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -789,6 +778,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(fetchEvent, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -806,6 +796,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(emptyBodyReq, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -820,6 +811,7 @@ describe('dbAuth', () => { }) dbAuth = new DbAuthHandler(emptyStringReq, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -842,6 +834,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -864,6 +857,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(user.resetToken).toEqual(undefined) expect(user.resetTokenExpiresAt).toEqual(undefined) @@ -901,6 +895,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.forgotPassword() expectLoggedOutResponse(response) @@ -921,6 +916,7 @@ describe('dbAuth', () => { expect(handlerUser.id).toEqual(user.id) } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.forgotPassword() expect.assertions(1) }) @@ -936,14 +932,16 @@ describe('dbAuth', () => { body, }) - options.forgotPassword.handler = (handlerUser) => { - // user should have the raw resetToken NOT the hash + options.forgotPassword.handler = (handlerUser, token) => { + // tokens should be the raw resetToken NOT the hash // resetToken consists of 16 base64 characters - expect(handlerUser.resetToken).toMatch(/^\w{16}$/) + expect(handlerUser.resetToken).toBeUndefined() + expect(token).toMatch(/^[A-Za-z0-9/+]{16}$/) } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.forgotPassword() - expect.assertions(1) + expect.assertions(2) }) it('removes the token from the forgotPassword response', async () => { @@ -961,6 +959,7 @@ describe('dbAuth', () => { return handlerUser } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.forgotPassword() const jsonResponse = JSON.parse(response[0]) @@ -981,6 +980,7 @@ describe('dbAuth', () => { // invalid db client const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() dbAuth.dbAccessor = undefined try { @@ -1008,6 +1008,7 @@ describe('dbAuth', () => { options.login.enabled = false const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1035,6 +1036,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1058,6 +1060,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1079,6 +1082,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1103,6 +1107,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1128,6 +1133,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.login() }) @@ -1145,6 +1151,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() } catch (e) { @@ -1168,6 +1175,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() } catch (e) { @@ -1187,10 +1195,11 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.login() - expect(response[0]).toEqual({ id: user.id }) + expect(response[0].id).toEqual(user.id) }) it('returns a CSRF token in the header', async () => { @@ -1204,6 +1213,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.login() expect(response[1]['csrf-token']).toMatch(UUID_REGEX) @@ -1220,6 +1230,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.login() @@ -1237,6 +1248,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.login() @@ -1244,8 +1256,8 @@ describe('dbAuth', () => { }) it('login db check is called with insensitive string when user has provided one in LoginFlowOptions', async () => { - jest.clearAllMocks() - const spy = jest.spyOn(db.user, 'findFirst') + vi.clearAllMocks() + const spy = vi.spyOn(db.user, 'findFirst') options.signup.usernameMatch = 'insensitive' options.login.usernameMatch = 'insensitive' @@ -1262,6 +1274,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1277,8 +1290,8 @@ describe('dbAuth', () => { }) it('login db check is not called with insensitive string when user has not provided one in LoginFlowOptions', async () => { - jest.clearAllMocks() - const spy = jest.spyOn(db.user, 'findFirst') + vi.clearAllMocks() + const spy = vi.spyOn(db.user, 'findFirst') delete options.signup.usernameMatch delete options.login.usernameMatch @@ -1295,6 +1308,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.login() @@ -1309,6 +1323,7 @@ describe('dbAuth', () => { describe('logout', () => { it('returns set-cookie header for removing session', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth.logout() expectLoggedOutResponse(response) @@ -1330,6 +1345,7 @@ describe('dbAuth', () => { options.resetPassword.enabled = false const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1357,6 +1373,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1373,6 +1390,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(emptyBody, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1386,6 +1404,7 @@ describe('dbAuth', () => { body: JSON.stringify({ resetToken: ' ' }), }) dbAuth = new DbAuthHandler(emptyString, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1403,6 +1422,7 @@ describe('dbAuth', () => { body: JSON.stringify({ resetToken: '1234' }), }) let dbAuth = new DbAuthHandler(noPwd, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1416,6 +1436,7 @@ describe('dbAuth', () => { body: JSON.stringify({ resetToken: '1234', password: ' ' }), }) dbAuth = new DbAuthHandler(pwdEmpty, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1434,6 +1455,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1460,6 +1482,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1489,6 +1512,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1523,6 +1547,7 @@ describe('dbAuth', () => { options.resetPassword.allowReusedPassword = false let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).rejects.toThrow( dbAuthError.ReusedPasswordError @@ -1550,6 +1575,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).resolves.not.toThrow() }) @@ -1573,6 +1599,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).resolves.not.toThrow() @@ -1604,6 +1631,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).resolves.not.toThrow() @@ -1637,6 +1665,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.resetPassword() expect.assertions(1) @@ -1662,6 +1691,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.resetPassword() @@ -1688,6 +1718,7 @@ describe('dbAuth', () => { }) let dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.resetPassword() @@ -1711,6 +1742,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.signup()).rejects.toThrow('Cannot signup') @@ -1729,6 +1761,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.signup() @@ -1755,6 +1788,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.signup() @@ -1781,6 +1815,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.signup() @@ -1807,6 +1842,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(() => dbAuth.signup()).not.toThrow() }) @@ -1824,6 +1860,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(() => dbAuth.signup()).not.toThrow() }) @@ -1841,6 +1878,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.signup() const newUserCount = await db.user.count() @@ -1868,6 +1906,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.signup() @@ -1887,7 +1926,6 @@ describe('dbAuth', () => { JSON.stringify({ id: user.id }) + ';' + 'token' ) - const justEncryptedSession = cookie.split('session=')[1] const headers = { cookie, } @@ -1899,19 +1937,20 @@ describe('dbAuth', () => { req.headers.get('cookie') const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.getToken() - expect(response[0]).toEqual(justEncryptedSession) + expect(response[0]).toEqual(user.id) }) it('returns nothing if user is not logged in', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.getToken() expect(response[0]).toEqual('') }) - // @TODO(Rob) HELP, change in behaviour it('returns any other error', async () => { req = { headers: { @@ -1922,12 +1961,12 @@ describe('dbAuth', () => { } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.getToken() expect(response[0]).toEqual('{"error":"User not found"}') }) - // @TODO(Rob) HELP, change in behaviour it('re-encrypts the session cookie if using the legacy algorithm', async () => { await createDbUser({ id: 7 }) req = { @@ -1940,6 +1979,7 @@ describe('dbAuth', () => { 'QKxN2vFSHAf94XYynK8LUALfDuDSdFowG6evfkFX8uszh4YZqhTiqEdshrhWbwbw' const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const [userId, headers] = await dbAuth.getToken() expect(userId).toEqual(7) @@ -1950,11 +1990,7 @@ describe('dbAuth', () => { }) }) - // @TODO: Studio should not use body to send auth impersonation details - // @TODO: Studio should not use body to send auth impersonation details - // @TODO: Studio should not use body to send auth impersonation details - // @TODO: Studio should not use body to send auth impersonation details - describe.skip('When a developer has set GraphiQL headers to mock a session cookie', () => { + describe('When a developer has set GraphiQL headers to mock a session cookie', () => { describe('when in development environment', () => { const curNodeEnv = process.env.NODE_ENV @@ -1968,44 +2004,49 @@ describe('dbAuth', () => { expect(process.env.NODE_ENV).toBe('test') }) - it('authenticates the user based on GraphiQL headers when no event.headers present', async () => { - // setup graphiQL header cookie in extensions + it('authenticates the user based on GraphiQL impersonated headers when no cookie present', async () => { + // Making Fetch API Requests with GraphiQL Headers in the body does not work because it's async + // but we can set the new 'rw-studio-impersonation-cookie' header const dbUser = await createDbUser() - req.body = JSON.stringify({ - extensions: { - headers: { - 'auth-provider': 'dbAuth', - cookie: encryptToCookie(JSON.stringify({ id: dbUser.id })), - authorization: 'Bearer ' + dbUser.id, - }, - }, + const headers = { + 'auth-provider': 'dbAuth', + 'rw-studio-impersonation-cookie': encryptToCookie( + JSON.stringify({ id: dbUser.id }) + ), + } + + const req = new Request('http://localhost:8910/_rw_mw', { + method: 'POST', + headers, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const user = await dbAuth._getCurrentUser() expect(user.id).toEqual(dbUser.id) }) - it('Cookie from GraphiQLHeaders takes precedence over event headers when authenticating user', async () => { + it('Cookie from GraphiQLHeaders takes precedence over real headers when authenticating user', async () => { // setup session cookie in GraphiQL header const dbUser = await createDbUser() const dbUserId = dbUser.id - req.body = JSON.stringify({ - extensions: { - headers: { - 'auth-provider': 'dbAuth', - cookie: encryptToCookie(JSON.stringify({ id: dbUserId })), - authorization: 'Bearer ' + dbUserId, - }, + const req = new Request('http://localhost:8910/_rw_mw', { + method: 'POST', + headers: { + 'auth-provider': 'dbAuth', + authorization: 'Bearer ' + dbUserId, + cookie: encryptToCookie(JSON.stringify({ id: 9999999999 })), // The "real" cookie with an invalid userId + // 👇 The impersonated header takes precendence + 'rw-studio-impersonation-cookie': encryptToCookie( + JSON.stringify({ id: dbUser.id }) + ), }, }) - // create session cookie in event header - req.headers.cookie = encryptToCookie(JSON.stringify({ id: 9999999999 })) - // should read session from graphiQL header, not from cookie const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const user = await dbAuth._getCurrentUser() expect(user.id).toEqual(dbUserId) }) @@ -2016,18 +2057,20 @@ describe('dbAuth', () => { const dbUser = await createDbUser() const dbUserId = dbUser.id - req.body = JSON.stringify({ - extensions: { - headers: { - 'auth-provider': 'dbAuth', - cookie: encryptToCookie(JSON.stringify({ id: dbUserId })), - authorization: 'Bearer ' + dbUserId, - }, + const req = new Request('http://localhost:8910/_rw_mw', { + method: 'POST', + headers: { + 'auth-provider': 'dbAuth', + 'rw-studio-impersonation-cookie': encryptToCookie( + JSON.stringify({ id: dbUserId }) + ), + authorization: 'Bearer ' + dbUserId, }, }) try { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth._getCurrentUser() } catch (e) { expect(e.message).toEqual( @@ -2053,7 +2096,6 @@ describe('dbAuth', () => { expect.assertions(1) }) - // @TODO(Rob) Failing test here it('throws an error if WebAuthn is disabled', async () => { const req = new Request('http://localhost:8910/_rw_mw', { method: 'POST', @@ -2062,6 +2104,7 @@ describe('dbAuth', () => { options.webAuthn.enabled = false const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.webAuthnAuthenticate()).rejects.toThrow( @@ -2080,6 +2123,7 @@ describe('dbAuth', () => { headers, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.webAuthnAuthenticate()).rejects.toThrow( @@ -2107,6 +2151,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.webAuthnAuthenticate()).rejects.toThrow( @@ -2134,6 +2179,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect.assertions(1) try { @@ -2172,6 +2218,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const [body, headers] = await dbAuth.webAuthnAuthenticate() @@ -2183,13 +2230,13 @@ describe('dbAuth', () => { }) describe('webAuthnAuthOptions', () => { - // @TODO(Rob): HELP, failing test here it('throws an error if user is not logged in', async () => { const req = new Request('http://localhost:8910/_rw_mw', { method: 'POST', headers: {}, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.webAuthnAuthOptions() @@ -2205,6 +2252,7 @@ describe('dbAuth', () => { } options.webAuthn.enabled = false const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.webAuthnAuthOptions() @@ -2224,6 +2272,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnAuthOptions() const regOptions = response[0] @@ -2251,6 +2300,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnAuthOptions() const regOptions = response[0] @@ -2272,6 +2322,7 @@ describe('dbAuth', () => { headers: {}, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.webAuthnRegOptions() @@ -2289,6 +2340,7 @@ describe('dbAuth', () => { options.webAuthn.enabled = false const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth.webAuthnRegOptions() @@ -2312,6 +2364,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnRegOptions() const regOptions = response[0] @@ -2343,6 +2396,7 @@ describe('dbAuth', () => { options.webAuthn.timeout = null const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnRegOptions() expect(response[0].timeout).toEqual(60000) @@ -2362,6 +2416,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnRegOptions() user = await db.user.findFirst({ where: { id: user.id } }) @@ -2384,6 +2439,7 @@ describe('dbAuth', () => { body: '{"method":"webAuthnRegister","id":"GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg","rawId":"GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg","response":{"attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVisSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAK3OAAI1vMYKZIsLJfHwVQMAKBqo2TrmGKaTmwQ3lZJ263AS5GmvYpkuRCScLQle-NGrFM9uLHQJhhalAQIDJiABIVggGIipTQt-gcoDPOpW6Zje_Av9C0-jWb2R2PBmXJJL-c8iWCC76wxo3uzG8cPqb0A8Vij-dqMbrEytEHjuFOtiQ2dt8A","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSHVHUHJRcUs3ZjUzTkx3TVpNc3RfREw5RGlnMkJCaXZEWVdXcGF3SVBWTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9"},"type":"public-key","clientExtensionResults":{},"transports":["internal"]}', } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.webAuthnRegister() @@ -2415,6 +2471,7 @@ describe('dbAuth', () => { ), } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth.webAuthnRegister() @@ -2501,6 +2558,7 @@ describe('dbAuth', () => { ...options, cookie: { Secure: true }, }) + const attributes = dbAuth._cookieAttributes({}) expect(attributes[0]).toEqual('Secure') @@ -2547,8 +2605,9 @@ describe('dbAuth', () => { }) describe('_createSessionHeader()', () => { - it('returns a Set-Cookie header', () => { + it('returns a Set-Cookie header', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const headers = dbAuth._createSessionHeader({ foo: 'bar' }, 'abcd') expect(Object.keys(headers).length).toEqual(1) @@ -2564,23 +2623,27 @@ describe('dbAuth', () => { }) }) - // @TODO(Rob): Not used yet. - describe.skip('_validateCsrf()', () => { - it('returns true if session and header token match', () => { + describe('_validateCsrf()', () => { + it('returns true if session and header token match', async () => { const data = { foo: 'bar' } const token = 'abcd' - req = { + + const req = new Request('http://localhost:8910/_rw_mw', { + method: 'POST', headers: { cookie: encryptToCookie(JSON.stringify(data) + ';' + token), 'csrf-token': token, }, - } + }) + const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() + const output = await dbAuth._validateCsrf() - expect(dbAuth._validateCsrf()).toEqual(true) + expect(output).toEqual(true) }) - it('throws an error if session and header token do not match', () => { + it('throws an error if session and header token do not match', async () => { const data = { foo: 'bar' } const token = 'abcd' req = { @@ -2590,16 +2653,18 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() - expect(() => { - dbAuth._validateCsrf() - }).toThrow(dbAuthError.CsrfTokenMismatchError) + expect(async () => { + await dbAuth._validateCsrf() + }).rejects.toThrow(dbAuthError.CsrfTokenMismatchError) }) }) describe('_verifyUser()', () => { it('throws an error if username is missing', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._verifyUser(null, 'password') @@ -2621,6 +2686,7 @@ describe('dbAuth', () => { it('throws an error if password is missing', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._verifyUser('username') @@ -2654,6 +2720,7 @@ describe('dbAuth', () => { const defaultMessage = options.login.errors.usernameOrPasswordMissing delete options.login.errors.usernameOrPasswordMissing const dbAuth1 = new DbAuthHandler(req, context, options) + await dbAuth1.init() try { await dbAuth1._verifyUser(null, 'password') } catch (e) { @@ -2676,6 +2743,7 @@ describe('dbAuth', () => { it('throws a default error message if user is not found', async () => { delete options.login.errors.usernameNotFound const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._verifyUser('username', 'password') } catch (e) { @@ -2689,6 +2757,7 @@ describe('dbAuth', () => { it('throws a custom error message if user is not found', async () => { options.login.errors.usernameNotFound = 'Cannot find ${username}' const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._verifyUser('Alice', 'password') @@ -2704,6 +2773,7 @@ describe('dbAuth', () => { delete options.login.errors.incorrectPassword const dbUser = await createDbUser() const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._verifyUser(dbUser.email, 'incorrect') @@ -2719,6 +2789,7 @@ describe('dbAuth', () => { options.login.errors.incorrectPassword = 'Wrong password for ${username}' const dbUser = await createDbUser() const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._verifyUser(dbUser.email, 'incorrect') @@ -2734,6 +2805,7 @@ describe('dbAuth', () => { const dbUser = await createDbUser() // invalid db client const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() dbAuth.dbAccessor = undefined try { @@ -2748,6 +2820,7 @@ describe('dbAuth', () => { it('returns the user with matching username and password', async () => { const dbUser = await createDbUser() const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const user = await dbAuth._verifyUser(dbUser.email, 'password') expect(user.id).toEqual(dbUser.id) @@ -2761,6 +2834,7 @@ describe('dbAuth', () => { salt: '2ef27f4073c603ba8b7807c6de6d6a89', }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const user = await dbAuth._verifyUser(dbUser.email, 'password') expect(user.id).toEqual(dbUser.id) @@ -2774,6 +2848,7 @@ describe('dbAuth', () => { salt: '2ef27f4073c603ba8b7807c6de6d6a89', }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth._verifyUser(dbUser.email, 'password') const user = await db.user.findFirst({ where: { id: dbUser.id } }) @@ -2789,6 +2864,7 @@ describe('dbAuth', () => { describe('_getCurrentUser()', () => { it('throw an error if user is not logged in', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._getCurrentUser() @@ -2811,6 +2887,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._getCurrentUser() @@ -2836,6 +2913,7 @@ describe('dbAuth', () => { // invalid db client const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() dbAuth.dbAccessor = undefined try { @@ -2861,6 +2939,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const user = await dbAuth._getCurrentUser() expect(user.id).toEqual(dbUser.id) @@ -2884,6 +2963,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2911,6 +2991,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2923,7 +3004,7 @@ describe('dbAuth', () => { }) it('createUser db check is called with insensitive string when user has provided one in SignupFlowOptions', async () => { - const spy = jest.spyOn(db.user, 'findFirst') + const spy = vi.spyOn(db.user, 'findFirst') options.signup.usernameMatch = 'insensitive' const dbUser = await createDbUser() @@ -2936,6 +3017,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() await dbAuth._createUser() expect(spy).toHaveBeenCalled() @@ -2947,11 +3029,11 @@ describe('dbAuth', () => { }) it('createUser db check is not called with insensitive string when user has not provided one in SignupFlowOptions', async () => { - jest.resetAllMocks() - jest.clearAllMocks() + vi.resetAllMocks() + vi.clearAllMocks() const defaultMessage = options.signup.errors.usernameTaken - const spy = jest.spyOn(db.user, 'findFirst') + const spy = vi.spyOn(db.user, 'findFirst') delete options.signup.usernameMatch const dbUser = await createDbUser() @@ -2965,6 +3047,7 @@ describe('dbAuth', () => { body, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2996,6 +3079,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -3021,6 +3105,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -3045,6 +3130,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() } catch (e) { @@ -3068,6 +3154,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -3094,6 +3181,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() try { const user = await dbAuth._createUser() @@ -3115,6 +3203,7 @@ describe('dbAuth', () => { headers: {}, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(await dbAuth._getAuthMethod()).toEqual('logout') }) @@ -3127,6 +3216,7 @@ describe('dbAuth', () => { headers: {}, } const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(await dbAuth._getAuthMethod()).toEqual('signup') }) @@ -3138,14 +3228,16 @@ describe('dbAuth', () => { headers: {}, }) const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(await dbAuth._getAuthMethod()).toBeUndefined() }) }) describe('validateField', () => { - it('checks for the presence of a field', () => { + it('checks for the presence of a field', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(() => { dbAuth._validateField('username', null) @@ -3158,24 +3250,27 @@ describe('dbAuth', () => { }).toThrow(dbAuth.FieldRequiredError) }) - it('passes validation if everything is present', () => { + it('passes validation if everything is present', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() expect(dbAuth._validateField('username', 'cannikin')).toEqual(true) }) }) describe('logoutResponse', () => { - it('returns the response array necessary to log user out', () => { + it('returns the response array necessary to log user out', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const [body, headers] = dbAuth._logoutResponse() expect(body).toEqual('') expect(headers['set-cookie']).toMatch(/^session=;/) }) - it('can accept an object to return in the body', () => { + it('can accept an object to return in the body', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const [body, _headers] = dbAuth._logoutResponse({ error: 'error message', }) @@ -3185,29 +3280,33 @@ describe('dbAuth', () => { }) describe('ok', () => { - it('returns a 200 response by default', () => { + it('returns a 200 response by default', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth._ok('', {}) expect(response.statusCode).toEqual(200) }) - it('can return other status codes', () => { + it('can return other status codes', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth._ok('', {}, { statusCode: 201 }) expect(response.statusCode).toEqual(201) }) - it('stringifies a JSON body', () => { + it('stringifies a JSON body', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth._ok({ foo: 'bar' }, {}, { statusCode: 201 }) expect(response.body).toEqual('{"foo":"bar"}') }) - it('does not stringify a body that is a string already', () => { + it('does not stringify a body that is a string already', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth._ok('{"foo":"bar"}', {}, { statusCode: 201 }) expect(response.body).toEqual('{"foo":"bar"}') @@ -3215,8 +3314,9 @@ describe('dbAuth', () => { }) describe('_notFound', () => { - it('returns a 404 response', () => { + it('returns a 404 response', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth._notFound() expect(response.statusCode).toEqual(404) @@ -3225,8 +3325,9 @@ describe('dbAuth', () => { }) describe('_badRequest', () => { - it('returns a 400 response', () => { + it('returns a 400 response', async () => { const dbAuth = new DbAuthHandler(req, context, options) + await dbAuth.init() const response = dbAuth._badRequest('bad') expect(response.statusCode).toEqual(400) diff --git a/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.test.js b/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.test.js index cb3a030e7e3f..72ba44f25e9e 100644 --- a/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.test.js +++ b/packages/auth-providers/dbAuth/api/src/__tests__/DbAuthHandler.test.js @@ -1,6 +1,17 @@ import crypto from 'node:crypto' import path from 'node:path' +import { + vi, + describe, + it, + expect, + beforeAll, + afterAll, + beforeEach, + afterEach, +} from 'vitest' + import { DbAuthHandler } from '../DbAuthHandler' import * as dbAuthError from '../errors' import { hashToken } from '../shared' @@ -136,7 +147,7 @@ let event, context, options describe('dbAuth', () => { beforeEach(() => { // hide deprecation warnings during test - jest.spyOn(console, 'warn').mockImplementation(() => {}) + vi.spyOn(console, 'warn').mockImplementation(() => {}) // encryption key so results are consistent regardless of settings in .env process.env.SESSION_SECRET = SESSION_SECRET delete process.env.DBAUTH_COOKIE_DOMAIN @@ -221,7 +232,7 @@ describe('dbAuth', () => { }) afterEach(async () => { - jest.spyOn(console, 'warn').mockRestore() + vi.spyOn(console, 'warn').mockRestore() await db.user.deleteMany({ where: { email: 'rob@redwoodjs.com' }, }) @@ -250,22 +261,25 @@ describe('dbAuth', () => { }) describe('dbAccessor', () => { - it('returns the prisma db accessor for a model', () => { + it('returns the prisma db accessor for a model', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(dbAuth.dbAccessor).toEqual(db.user) }) }) describe('dbCredentialAccessor', () => { - it('returns the prisma db accessor for a UserCredential model', () => { + it('returns the prisma db accessor for a UserCredential model', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(dbAuth.dbCredentialAccessor).toEqual(db.userCredential) }) }) describe('sessionExpiresDate', () => { - it('returns a date in the future as a UTCString', () => { + it('returns a date in the future as a UTCString', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const expiresAt = new Date() expiresAt.setSeconds(expiresAt.getSeconds() + options.login.expires) @@ -274,8 +288,9 @@ describe('dbAuth', () => { }) describe('webAuthnExpiresDate', () => { - it('returns a date in the future as a UTCString', () => { + it('returns a date in the future as a UTCString', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const expiresAt = new Date() expiresAt.setSeconds(expiresAt.getSeconds() + options.webAuthn.expires) @@ -284,8 +299,9 @@ describe('dbAuth', () => { }) describe('_deleteSessionHeader', () => { - it('returns a Set-Cookie header to delete the session cookie', () => { + it('returns a Set-Cookie header to delete the session cookie', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const headers = dbAuth._deleteSessionHeader expect(Object.keys(headers).length).toEqual(1) @@ -294,8 +310,8 @@ describe('dbAuth', () => { }) }) - describe.only('constructor', () => { - it('initializes some variables with passed values', () => { + describe('constructor', () => { + it('initializes some variables with passed values', async () => { event = { headers: {} } context = { foo: 'bar' } options = { @@ -315,6 +331,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(dbAuth.event).toEqual(event) expect(dbAuth.options).toEqual(options) @@ -523,9 +540,14 @@ describe('dbAuth', () => { }) }) - it.skip('parses an empty plain text body and still sets params', async () => { - // @TODO(Rob): This test is failing due to refactor, not sure its necessary - event = { isBase64Encoded: false, headers: {}, body: '' } + it('parses an empty plain text body and still sets params', async () => { + event = { + isBase64Encoded: false, + headers: { + bazomga: 'yo', + }, + body: '', + } context = { foo: 'bar' } const dbAuth = new DbAuthHandler(event, context, options) await dbAuth.init() @@ -533,9 +555,7 @@ describe('dbAuth', () => { expect(dbAuth.normalizedRequest.jsonBody).toEqual({}) }) - it.skip('parses params from an undefined body when isBase64Encoded == false', async () => { - // @TODO(Rob): This test is failing due to refactor, not sure its necessary - + it('parses params from an undefined body when isBase64Encoded == false', async () => { event = { isBase64Encoded: false, headers: {}, @@ -555,6 +575,7 @@ describe('dbAuth', () => { } const dbAuth = new DbAuthHandler(event, context, options) await dbAuth.init() + expect(dbAuth.normalizedRequest.jsonBody).toEqual({ foo: 'bar', baz: 123, @@ -562,7 +583,6 @@ describe('dbAuth', () => { }) it('parses params from an undefined body when isBase64Encoded == true', async () => { - // @TODO(Rob): Not sure this is necessary any more? event = { isBase64Encoded: true, headers: {}, @@ -571,11 +591,10 @@ describe('dbAuth', () => { const dbAuth = new DbAuthHandler(event, context, options) await dbAuth.init() - expect(dbAuth.normalizedRequest.jsonBody).toEqual(undefined) + expect(dbAuth.normalizedRequest.jsonBody).toEqual({}) }) it('parses params from an empty body when isBase64Encoded == true', async () => { - // @TODO(Rob): Not sure this is necessary any more? event = { isBase64Encoded: true, headers: {}, @@ -585,7 +604,7 @@ describe('dbAuth', () => { const dbAuth = new DbAuthHandler(event, context, options) await dbAuth.init() - expect(dbAuth.normalizedRequest.jsonBody).toEqual(undefined) + expect(dbAuth.normalizedRequest.jsonBody).toEqual({}) }) it('sets header-based CSRF token', async () => { @@ -597,15 +616,16 @@ describe('dbAuth', () => { ) }) - it('sets session variables to nothing if session cannot be decrypted', () => { + it('sets session variables to nothing if session cannot be decrypted', async () => { event = { headers: { 'csrf-token': 'qwerty' } } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(dbAuth.session).toBeUndefined() expect(dbAuth.sessionCsrfToken).toBeUndefined() }) - it('sets session variables to valid session data', () => { + it('sets session variables to valid session data', async () => { event = { headers: { cookie: @@ -613,6 +633,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(dbAuth.session).toEqual({ foo: 'bar' }) expect(dbAuth.sessionCsrfToken).toEqual('abcd') @@ -633,6 +654,7 @@ describe('dbAuth', () => { event.httpMethod = 'GET' event.headers.cookie = 'session=invalid' const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.invoke() expect(response.headers['set-cookie']).toEqual(LOGOUT_COOKIE) @@ -644,6 +666,7 @@ describe('dbAuth', () => { event.headers.cookie = 'session=ko6iXKV11DSjb6kFJ4iwcf1FEqa5wPpbL1sdtKiV51Y=|cQaYkOPG/r3ILxWiFiz90w==' const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.invoke() expect(response.statusCode).toEqual(404) @@ -655,6 +678,7 @@ describe('dbAuth', () => { event.headers.cookie = 'session=ko6iXKV11DSjb6kFJ4iwcf1FEqa5wPpbL1sdtKiV51Y=|cQaYkOPG/r3ILxWiFiz90w==' const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.invoke() expect(response.statusCode).toEqual(404) @@ -666,7 +690,8 @@ describe('dbAuth', () => { event.headers.cookie = 'session=ko6iXKV11DSjb6kFJ4iwcf1FEqa5wPpbL1sdtKiV51Y=|cQaYkOPG/r3ILxWiFiz90w==' const dbAuth = new DbAuthHandler(event, context, options) - dbAuth.logout = jest.fn(() => { + await dbAuth.init() + dbAuth.logout = vi.fn(() => { throw Error('Logout error') }) const response = await dbAuth.invoke() @@ -686,7 +711,9 @@ describe('dbAuth', () => { credentials: true, }, }) - dbAuth.logout = jest.fn(() => { + await dbAuth.init() + + dbAuth.logout = vi.fn(() => { throw Error('Logout error') }) const response = await dbAuth.invoke() @@ -704,7 +731,8 @@ describe('dbAuth', () => { event.headers.cookie = 'session=ko6iXKV11DSjb6kFJ4iwcf1FEqa5wPpbL1sdtKiV51Y=|cQaYkOPG/r3ILxWiFiz90w==' const dbAuth = new DbAuthHandler(event, context, options) - dbAuth.logout = jest.fn(() => ['body', { foo: 'bar' }]) + await dbAuth.init() + dbAuth.logout = vi.fn(() => ['body', { foo: 'bar' }]) const response = await dbAuth.invoke() expect(dbAuth.logout).toHaveBeenCalled() @@ -726,6 +754,7 @@ describe('dbAuth', () => { }) options.forgotPassword.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -747,6 +776,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -759,7 +789,8 @@ describe('dbAuth', () => { it('throws an error if username is blank', async () => { // missing completely event.body = JSON.stringify({}) - let dbAuth = new DbAuthHandler(event, context, options) + const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -769,10 +800,10 @@ describe('dbAuth', () => { // empty string event.body = JSON.stringify({ username: ' ' }) - dbAuth = new DbAuthHandler(event, context, options) - + const dbAuth2 = new DbAuthHandler(event, context, options) + await dbAuth2.init() try { - await dbAuth.forgotPassword() + await dbAuth2.forgotPassword() } catch (e) { expect(e).toBeInstanceOf(dbAuthError.UsernameRequiredError) } @@ -786,6 +817,7 @@ describe('dbAuth', () => { username: 'notfound', }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.forgotPassword() @@ -802,6 +834,7 @@ describe('dbAuth', () => { username: user.email, }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(user.resetToken).toEqual(undefined) expect(user.resetTokenExpiresAt).toEqual(undefined) @@ -834,6 +867,7 @@ describe('dbAuth', () => { username: user.email, }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.forgotPassword() expectLoggedOutResponse(response) @@ -849,6 +883,7 @@ describe('dbAuth', () => { expect(token).toMatch(/^[A-Za-z0-9/+]{16}$/) } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.forgotPassword() expect.assertions(2) }) @@ -865,6 +900,7 @@ describe('dbAuth', () => { expect(token).toMatch(/^[A-Za-z0-9/+]{16}$/) } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.forgotPassword() expect.assertions(2) }) @@ -878,6 +914,7 @@ describe('dbAuth', () => { return handlerUser } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.forgotPassword() const jsonResponse = JSON.parse(response[0]) @@ -892,14 +929,13 @@ describe('dbAuth', () => { }) // invalid db client const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() dbAuth.dbAccessor = undefined - try { await dbAuth.forgotPassword() } catch (e) { expect(e).toBeInstanceOf(dbAuthError.GenericError) } - expect.assertions(1) }) }) @@ -913,6 +949,7 @@ describe('dbAuth', () => { }) options.login.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -934,6 +971,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -952,6 +990,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -969,6 +1008,7 @@ describe('dbAuth', () => { password: 'incorrect', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -989,6 +1029,7 @@ describe('dbAuth', () => { throw new Error('Cannot log in') } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1010,6 +1051,7 @@ describe('dbAuth', () => { return user } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.login() }) @@ -1023,6 +1065,7 @@ describe('dbAuth', () => { return null } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() } catch (e) { @@ -1042,6 +1085,7 @@ describe('dbAuth', () => { return { name: 'Rob' } } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() } catch (e) { @@ -1057,6 +1101,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.login() @@ -1070,6 +1115,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.login() expect(response[1]['csrf-token']).toMatch(UUID_REGEX) @@ -1082,6 +1128,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.login() @@ -1095,6 +1142,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.login() @@ -1102,8 +1150,8 @@ describe('dbAuth', () => { }) it('login db check is called with insensitive string when user has provided one in LoginFlowOptions', async () => { - jest.clearAllMocks() - const spy = jest.spyOn(db.user, 'findFirst') + vi.clearAllMocks() + const spy = vi.spyOn(db.user, 'findFirst') options.signup.usernameMatch = 'insensitive' options.login.usernameMatch = 'insensitive' @@ -1115,6 +1163,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.login() @@ -1130,8 +1179,8 @@ describe('dbAuth', () => { }) it('login db check is not called with insensitive string when user has not provided one in LoginFlowOptions', async () => { - jest.clearAllMocks() - const spy = jest.spyOn(db.user, 'findFirst') + vi.clearAllMocks() + const spy = vi.spyOn(db.user, 'findFirst') delete options.signup.usernameMatch delete options.login.usernameMatch @@ -1143,6 +1192,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.login() @@ -1157,6 +1207,7 @@ describe('dbAuth', () => { describe('logout', () => { it('returns set-cookie header for removing session', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth.logout() expectLoggedOutResponse(response) @@ -1172,6 +1223,7 @@ describe('dbAuth', () => { }) options.resetPassword.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1193,6 +1245,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1205,7 +1258,7 @@ describe('dbAuth', () => { // missing completely event.body = JSON.stringify({}) let dbAuth = new DbAuthHandler(event, context, options) - + await dbAuth.init() try { await dbAuth.resetPassword() } catch (e) { @@ -1215,6 +1268,7 @@ describe('dbAuth', () => { // empty string event.body = JSON.stringify({ resetToken: ' ' }) dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1229,6 +1283,7 @@ describe('dbAuth', () => { // missing completely event.body = JSON.stringify({ resetToken: '1234' }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1239,6 +1294,7 @@ describe('dbAuth', () => { // empty string event.body = JSON.stringify({ resetToken: '1234', password: ' ' }) dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1252,6 +1308,7 @@ describe('dbAuth', () => { it('throws an error if no user found with resetToken', async () => { event.body = JSON.stringify({ resetToken: '1234', password: 'password' }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1273,6 +1330,7 @@ describe('dbAuth', () => { event.body = JSON.stringify({ resetToken: '1234', password: 'password1' }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1297,6 +1355,7 @@ describe('dbAuth', () => { password: 'password1', }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.resetPassword() @@ -1326,6 +1385,7 @@ describe('dbAuth', () => { }) options.resetPassword.allowReusedPassword = false let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).rejects.toThrow( dbAuthError.ReusedPasswordError @@ -1348,6 +1408,7 @@ describe('dbAuth', () => { }) options.resetPassword.allowReusedPassword = true let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).resolves.not.toThrow() }) @@ -1366,6 +1427,7 @@ describe('dbAuth', () => { password: 'new-password', }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).resolves.not.toThrow() @@ -1392,6 +1454,7 @@ describe('dbAuth', () => { password: 'new-password', }) let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await expect(dbAuth.resetPassword()).resolves.not.toThrow() @@ -1420,6 +1483,7 @@ describe('dbAuth', () => { expect(handlerUser.id).toEqual(user.id) } let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.resetPassword() expect.assertions(1) @@ -1440,6 +1504,7 @@ describe('dbAuth', () => { }) options.resetPassword.handler = () => false let dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.resetPassword() @@ -1461,7 +1526,7 @@ describe('dbAuth', () => { }) options.resetPassword.handler = () => true let dbAuth = new DbAuthHandler(event, context, options) - + await dbAuth.init() const response = await dbAuth.resetPassword() expectLoggedInResponse(response) @@ -1479,6 +1544,7 @@ describe('dbAuth', () => { throw Error('Cannot signup') } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.signup()).rejects.toThrow('Cannot signup') @@ -1492,6 +1558,7 @@ describe('dbAuth', () => { }) options.signup.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.signup() @@ -1513,6 +1580,7 @@ describe('dbAuth', () => { flowNotEnabled: 'Custom flow not enabled error', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.signup() @@ -1534,6 +1602,7 @@ describe('dbAuth', () => { } } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.signup() @@ -1555,6 +1624,7 @@ describe('dbAuth', () => { } } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(() => dbAuth.signup()).not.toThrow() }) @@ -1567,6 +1637,7 @@ describe('dbAuth', () => { }) delete options.signup.passwordValidation const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(() => dbAuth.signup()).not.toThrow() }) @@ -1579,6 +1650,7 @@ describe('dbAuth', () => { }) const oldUserCount = await db.user.count() const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.signup() const newUserCount = await db.user.count() @@ -1601,6 +1673,7 @@ describe('dbAuth', () => { return 'Hello, world' } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.signup() @@ -1613,29 +1686,28 @@ describe('dbAuth', () => { }) }) - describe.only('getToken', () => { + describe('getToken', () => { it('returns the token from the cookie', async () => { const user = await createDbUser() const cookie = encryptToCookie( JSON.stringify({ id: user.id }) + ';' + 'token' ) - const justEncryptedSession = cookie.split('session=')[1] - event = { headers: { cookie, }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.getToken() - expect(response[0]).toEqual(justEncryptedSession) + expect(response[0]).toEqual(user.id) }) - // @TODO Rob HELP, change in behaviour it('returns nothing if user is not logged in', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.getToken() expect(response[0]).toEqual('') @@ -1651,12 +1723,12 @@ describe('dbAuth', () => { } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.getToken() expect(response[0]).toEqual('{"error":"User not found"}') }) - // @TODO Rob HELP, change in behaviour it('re-encrypts the session cookie if using the legacy algorithm', async () => { await createDbUser({ id: 7 }) event = { @@ -1669,6 +1741,7 @@ describe('dbAuth', () => { 'QKxN2vFSHAf94XYynK8LUALfDuDSdFowG6evfkFX8uszh4YZqhTiqEdshrhWbwbw' const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const [userId, headers] = await dbAuth.getToken() expect(userId).toEqual(7) @@ -1679,11 +1752,7 @@ describe('dbAuth', () => { }) }) - // @TODO: Studio should not use body to send auth impersonation details - // @TODO: Studio should not use body to send auth impersonation details - // @TODO: Studio should not use body to send auth impersonation details - // @TODO: Studio should not use body to send auth impersonation details - describe.skip('When a developer has set GraphiQL headers to mock a session cookie', () => { + describe('When a developer has set GraphiQL headers to mock a session cookie', () => { describe('when in development environment', () => { const curNodeEnv = process.env.NODE_ENV @@ -1711,6 +1780,7 @@ describe('dbAuth', () => { }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = await dbAuth._getCurrentUser() expect(user.id).toEqual(dbUser.id) }) @@ -1737,6 +1807,7 @@ describe('dbAuth', () => { // should read session from graphiQL header, not from cookie const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = await dbAuth._getCurrentUser() expect(user.id).toEqual(dbUserId) }) @@ -1759,6 +1830,7 @@ describe('dbAuth', () => { try { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth._getCurrentUser() } catch (e) { expect(e.message).toEqual( @@ -1790,6 +1862,7 @@ describe('dbAuth', () => { } options.webAuthn.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.webAuthnAuthenticate()).rejects.toThrow( @@ -1803,6 +1876,7 @@ describe('dbAuth', () => { body: '{"method":"webAuthnAuthenticate","id":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","rawId":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAA","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTHRnV3BoWUtfZU41clhjX0hkdlVMdk9xcFBXeW9SdmJtbDJQbzAwVUhhZyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEUCIQD3NOM7Aw0HxPw6EFGf86iwf2yd3p4NncNNLcjd-86zgwIgHuh80bLNV7EcwBi4IAcH57iueLg0X2gLtO5_Y6PMCFE","userHandle":"2"},"type":"public-key","clientExtensionResults":{}}', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.webAuthnAuthenticate()).rejects.toThrow( @@ -1827,6 +1901,7 @@ describe('dbAuth', () => { body: '{"method":"webAuthnAuthenticate","id":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","rawId":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAA","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTHRnV3BoWUtfZU41clhjX0hkdlVMdk9xcFBXeW9SdmJtbDJQbzAwVUhhZyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEUCIQD3NOM7Aw0HxPw6EFGf86iwf2yd3p4NncNNLcjd-86zgwIgHuh80bLNV7EcwBi4IAcH57iueLg0X2gLtO5_Y6PMCFE","userHandle":"2"},"type":"public-key","clientExtensionResults":{}}', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect.assertions(1) await expect(dbAuth.webAuthnAuthenticate()).rejects.toThrow( @@ -1851,6 +1926,7 @@ describe('dbAuth', () => { body: '{"method":"webAuthnAuthenticate","id":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","rawId":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAA","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTHRnV3BoWUtfZU41clhjX0hkdlVMdk9xcFBXeW9SdmJtbDJQbzAwVUhhZyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEUCIQD3NOM7Aw0HxPw6EFGf86iwf2yd3p4NncNNLcjd-86zgwIgHuh80bLNV7EcwBi4IAcH57iueLg0X2gLtO5_Y6PMCFE","userHandle":"2"},"type":"public-key","clientExtensionResults":{}}', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect.assertions(1) try { @@ -1887,6 +1963,7 @@ describe('dbAuth', () => { body: '{"method":"webAuthnAuthenticate","id":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","rawId":"CxMJqILwYufSaEQsJX6rKHw_LkMXAGU64PaKU55l6ejZ4FNO5kBLiA","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAA","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTHRnV3BoWUtfZU41clhjX0hkdlVMdk9xcFBXeW9SdmJtbDJQbzAwVUhhZyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEUCIQD3NOM7Aw0HxPw6EFGf86iwf2yd3p4NncNNLcjd-86zgwIgHuh80bLNV7EcwBi4IAcH57iueLg0X2gLtO5_Y6PMCFE","userHandle":"2"},"type":"public-key","clientExtensionResults":{}}', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const [body, headers] = await dbAuth.webAuthnAuthenticate() @@ -1903,6 +1980,7 @@ describe('dbAuth', () => { headers: {}, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.webAuthnAuthOptions() @@ -1918,6 +1996,7 @@ describe('dbAuth', () => { } options.webAuthn.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.webAuthnAuthOptions() @@ -1937,6 +2016,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnAuthOptions() const regOptions = response[0] @@ -1964,6 +2044,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnAuthOptions() const regOptions = response[0] @@ -1984,6 +2065,7 @@ describe('dbAuth', () => { headers: {}, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.webAuthnRegOptions() @@ -1999,6 +2081,7 @@ describe('dbAuth', () => { } options.webAuthn.enabled = false const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth.webAuthnRegOptions() @@ -2018,6 +2101,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnRegOptions() const regOptions = response[0] @@ -2045,6 +2129,7 @@ describe('dbAuth', () => { } options.webAuthn.timeout = null const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnRegOptions() expect(response[0].timeout).toEqual(60000) @@ -2060,6 +2145,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = await dbAuth.webAuthnRegOptions() user = await db.user.findFirst({ where: { id: user.id } }) @@ -2082,6 +2168,7 @@ describe('dbAuth', () => { body: '{"method":"webAuthnRegister","id":"GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg","rawId":"GqjZOuYYppObBDeVknbrcBLkaa9imS5EJJwtCV740asUz24sdAmGFg","response":{"attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVisSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAK3OAAI1vMYKZIsLJfHwVQMAKBqo2TrmGKaTmwQ3lZJ263AS5GmvYpkuRCScLQle-NGrFM9uLHQJhhalAQIDJiABIVggGIipTQt-gcoDPOpW6Zje_Av9C0-jWb2R2PBmXJJL-c8iWCC76wxo3uzG8cPqb0A8Vij-dqMbrEytEHjuFOtiQ2dt8A","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSHVHUHJRcUs3ZjUzTkx3TVpNc3RfREw5RGlnMkJCaXZEWVdXcGF3SVBWTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODkxMCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9"},"type":"public-key","clientExtensionResults":{},"transports":["internal"]}', } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.webAuthnRegister() @@ -2113,6 +2200,7 @@ describe('dbAuth', () => { ), } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth.webAuthnRegister() @@ -2149,14 +2237,16 @@ describe('dbAuth', () => { }) describe('_webAuthnCookie', () => { - it('returns the parts needed for the webAuthn cookie, defaulted to future expire', () => { + it('returns the parts needed for the webAuthn cookie, defaulted to future expire', async () => { const dbAuth = new DbAuthHandler({ headers: {} }, context, options) + await dbAuth.init() expect(dbAuth._webAuthnCookie('1234')).toMatch('webAuthn=1234;Expires=') }) - it('returns the parts needed for the expire the webAuthn cookie', () => { + it('returns the parts needed for the expire the webAuthn cookie', async () => { const dbAuth = new DbAuthHandler({ headers: {} }, context, options) + await dbAuth.init() expect(dbAuth._webAuthnCookie('1234', 'now')).toMatch( 'webAuthn=1234;Expires=Thu, 01 Jan 1970 00:00:00 GMT' @@ -2182,6 +2272,7 @@ describe('dbAuth', () => { }, } ) + const attributes = dbAuth._cookieAttributes({}) expect(attributes.length).toEqual(6) @@ -2245,8 +2336,9 @@ describe('dbAuth', () => { }) describe('_createSessionHeader()', () => { - it('returns a Set-Cookie header', () => { + it('returns a Set-Cookie header', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const headers = dbAuth._createSessionHeader({ foo: 'bar' }, 'abcd') expect(Object.keys(headers).length).toEqual(1) @@ -2263,7 +2355,7 @@ describe('dbAuth', () => { }) describe('_validateCsrf()', () => { - it('returns true if session and header token match', () => { + it('returns true if session and header token match', async () => { const data = { foo: 'bar' } const token = 'abcd' event = { @@ -2273,11 +2365,14 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() - expect(dbAuth._validateCsrf()).toEqual(true) + const output = await dbAuth._validateCsrf() + + expect(output).toEqual(true) }) - it('throws an error if session and header token do not match', () => { + it('throws an error if session and header token do not match', async () => { const data = { foo: 'bar' } const token = 'abcd' event = { @@ -2287,16 +2382,18 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() - expect(() => { - dbAuth._validateCsrf() - }).toThrow(dbAuthError.CsrfTokenMismatchError) + expect(async () => { + await dbAuth._validateCsrf() + }).rejects.toThrow(dbAuthError.CsrfTokenMismatchError) }) }) describe('_verifyUser()', () => { it('throws an error if username is missing', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._verifyUser(null, 'password') @@ -2318,25 +2415,23 @@ describe('dbAuth', () => { it('throws an error if password is missing', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._verifyUser('username') } catch (e) { expect(e).toBeInstanceOf(dbAuthError.UsernameAndPasswordRequiredError) } - try { await dbAuth._verifyUser('username', null) } catch (e) { expect(e).toBeInstanceOf(dbAuthError.UsernameAndPasswordRequiredError) } - try { await dbAuth._verifyUser('username', '') } catch (e) { expect(e).toBeInstanceOf(dbAuthError.UsernameAndPasswordRequiredError) } - try { await dbAuth._verifyUser('username', ' ') } catch (e) { @@ -2351,6 +2446,7 @@ describe('dbAuth', () => { const defaultMessage = options.login.errors.usernameOrPasswordMissing delete options.login.errors.usernameOrPasswordMissing const dbAuth1 = new DbAuthHandler(event, context, options) + await dbAuth1.init() try { await dbAuth1._verifyUser(null, 'password') } catch (e) { @@ -2360,7 +2456,6 @@ describe('dbAuth', () => { // custom error message options.login.errors.usernameOrPasswordMissing = 'Missing!' const customMessage = new DbAuthHandler(event, context, options) - try { await customMessage._verifyUser(null, 'password') } catch (e) { @@ -2373,6 +2468,7 @@ describe('dbAuth', () => { it('throws a default error message if user is not found', async () => { delete options.login.errors.usernameNotFound const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._verifyUser('username', 'password') } catch (e) { @@ -2386,6 +2482,7 @@ describe('dbAuth', () => { it('throws a custom error message if user is not found', async () => { options.login.errors.usernameNotFound = 'Cannot find ${username}' const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._verifyUser('Alice', 'password') @@ -2401,6 +2498,7 @@ describe('dbAuth', () => { delete options.login.errors.incorrectPassword const dbUser = await createDbUser() const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._verifyUser(dbUser.email, 'incorrect') @@ -2416,6 +2514,7 @@ describe('dbAuth', () => { options.login.errors.incorrectPassword = 'Wrong password for ${username}' const dbUser = await createDbUser() const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._verifyUser(dbUser.email, 'incorrect') @@ -2431,20 +2530,20 @@ describe('dbAuth', () => { const dbUser = await createDbUser() // invalid db client const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() dbAuth.dbAccessor = undefined - try { await dbAuth._verifyUser(dbUser.email, 'password') } catch (e) { expect(e).toBeInstanceOf(dbAuthError.GenericError) } - expect.assertions(1) }) it('returns the user with matching username and password', async () => { const dbUser = await createDbUser() const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = await dbAuth._verifyUser(dbUser.email, 'password') expect(user.id).toEqual(dbUser.id) @@ -2458,6 +2557,7 @@ describe('dbAuth', () => { salt: '2ef27f4073c603ba8b7807c6de6d6a89', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = await dbAuth._verifyUser(dbUser.email, 'password') expect(user.id).toEqual(dbUser.id) @@ -2471,6 +2571,7 @@ describe('dbAuth', () => { salt: '2ef27f4073c603ba8b7807c6de6d6a89', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth._verifyUser(dbUser.email, 'password') const user = await db.user.findFirst({ where: { id: dbUser.id } }) @@ -2486,13 +2587,13 @@ describe('dbAuth', () => { describe('_getCurrentUser()', () => { it('throw an error if user is not logged in', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._getCurrentUser() } catch (e) { expect(e).toBeInstanceOf(dbAuthError.NotLoggedInError) } - expect.assertions(1) }) @@ -2504,13 +2605,13 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._getCurrentUser() } catch (e) { expect(e).toBeInstanceOf(dbAuthError.UserNotFoundError) } - expect.assertions(1) }) @@ -2525,14 +2626,13 @@ describe('dbAuth', () => { } // invalid db client const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() dbAuth.dbAccessor = undefined - try { await dbAuth._getCurrentUser() } catch (e) { expect(e).toBeInstanceOf(dbAuthError.GenericError) } - expect.assertions(1) }) @@ -2546,6 +2646,7 @@ describe('dbAuth', () => { }, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = await dbAuth._getCurrentUser() expect(user.id).toEqual(dbUser.id) @@ -2562,6 +2663,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2583,6 +2685,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2595,7 +2698,7 @@ describe('dbAuth', () => { }) it('createUser db check is called with insensitive string when user has provided one in SignupFlowOptions', async () => { - const spy = jest.spyOn(db.user, 'findFirst') + const spy = vi.spyOn(db.user, 'findFirst') options.signup.usernameMatch = 'insensitive' const dbUser = await createDbUser() @@ -2604,6 +2707,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() await dbAuth._createUser() expect(spy).toHaveBeenCalled() @@ -2615,11 +2719,11 @@ describe('dbAuth', () => { }) it('createUser db check is not called with insensitive string when user has not provided one in SignupFlowOptions', async () => { - jest.resetAllMocks() - jest.clearAllMocks() + vi.resetAllMocks() + vi.clearAllMocks() const defaultMessage = options.signup.errors.usernameTaken - const spy = jest.spyOn(db.user, 'findFirst') + const spy = vi.spyOn(db.user, 'findFirst') delete options.signup.usernameMatch const dbUser = await createDbUser() @@ -2628,6 +2732,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2653,6 +2758,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2672,6 +2778,7 @@ describe('dbAuth', () => { password: 'password', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2690,6 +2797,7 @@ describe('dbAuth', () => { username: 'user@redwdoodjs.com', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() } catch (e) { @@ -2708,6 +2816,7 @@ describe('dbAuth', () => { username: 'user@redwdoodjs.com', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { await dbAuth._createUser() @@ -2727,6 +2836,7 @@ describe('dbAuth', () => { name: 'Rob', }) const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() try { const user = await dbAuth._createUser() @@ -2741,7 +2851,7 @@ describe('dbAuth', () => { }) describe('getAuthMethod', () => { - it('gets methodName out of the query string', () => { + it('gets methodName out of the query string', async () => { event = { path: '/.redwood/functions/auth', queryStringParameters: { method: 'logout' }, @@ -2749,11 +2859,12 @@ describe('dbAuth', () => { headers: {}, } const dbAuth = new DbAuthHandler(event, context, options) - - expect(dbAuth._getAuthMethod()).toEqual('logout') + await dbAuth.init() + const method = await dbAuth._getAuthMethod() + expect(method).toEqual('logout') }) - it('gets methodName out of a JSON body', () => { + it('gets methodName out of a JSON body', async () => { event = { path: '/.redwood/functions/auth', queryStringParameters: {}, @@ -2761,11 +2872,13 @@ describe('dbAuth', () => { headers: {}, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() + const method = await dbAuth._getAuthMethod() - expect(dbAuth._getAuthMethod()).toEqual('signup') + expect(method).toEqual('signup') }) - it('otherwise returns undefined', () => { + it('otherwise returns undefined', async () => { event = { path: '/.redwood/functions/auth', queryStringParameters: {}, @@ -2773,14 +2886,17 @@ describe('dbAuth', () => { headers: {}, } const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() - expect(dbAuth._getAuthMethod()).toBeUndefined() + const method = await dbAuth._getAuthMethod() + expect(method).toBeUndefined() }) }) describe('validateField', () => { - it('checks for the presence of a field', () => { + it('checks for the presence of a field', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(() => { dbAuth._validateField('username', null) @@ -2793,24 +2909,27 @@ describe('dbAuth', () => { }).toThrow(dbAuth.FieldRequiredError) }) - it('passes validation if everything is present', () => { + it('passes validation if everything is present', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() expect(dbAuth._validateField('username', 'cannikin')).toEqual(true) }) }) describe('logoutResponse', () => { - it('returns the response array necessary to log user out', () => { + it('returns the response array necessary to log user out', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const [body, headers] = dbAuth._logoutResponse() expect(body).toEqual('') expect(headers['set-cookie']).toMatch(/^session=;/) }) - it('can accept an object to return in the body', () => { + it('can accept an object to return in the body', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const [body, _headers] = dbAuth._logoutResponse({ error: 'error message', }) @@ -2820,29 +2939,33 @@ describe('dbAuth', () => { }) describe('ok', () => { - it('returns a 200 response by default', () => { + it('returns a 200 response by default', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth._ok('', {}) expect(response.statusCode).toEqual(200) }) - it('can return other status codes', () => { + it('can return other status codes', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth._ok('', {}, { statusCode: 201 }) expect(response.statusCode).toEqual(201) }) - it('stringifies a JSON body', () => { + it('stringifies a JSON body', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth._ok({ foo: 'bar' }, {}, { statusCode: 201 }) expect(response.body).toEqual('{"foo":"bar"}') }) - it('does not stringify a body that is a string already', () => { + it('does not stringify a body that is a string already', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth._ok('{"foo":"bar"}', {}, { statusCode: 201 }) expect(response.body).toEqual('{"foo":"bar"}') @@ -2850,8 +2973,9 @@ describe('dbAuth', () => { }) describe('_notFound', () => { - it('returns a 404 response', () => { + it('returns a 404 response', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth._notFound() expect(response.statusCode).toEqual(404) @@ -2860,8 +2984,9 @@ describe('dbAuth', () => { }) describe('_badRequest', () => { - it('returns a 400 response', () => { + it('returns a 400 response', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const response = dbAuth._badRequest('bad') expect(response.statusCode).toEqual(400) @@ -2870,8 +2995,9 @@ describe('dbAuth', () => { }) describe('_sanitizeUser', () => { - it('removes all but the default fields [id, email] on user', () => { + it('removes all but the default fields [id, email] on user', async () => { const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = { id: 1, email: 'rob@redwoodjs.com', @@ -2883,9 +3009,10 @@ describe('dbAuth', () => { expect(dbAuth._sanitizeUser(user).secret).toBeUndefined() }) - it('removes any fields not explictly allowed in allowedUserFields', () => { + it('removes any fields not explictly allowed in allowedUserFields', async () => { options.allowedUserFields = ['foo'] const dbAuth = new DbAuthHandler(event, context, options) + await dbAuth.init() const user = { id: 1, email: 'rob@redwoodjs.com', diff --git a/packages/auth-providers/dbAuth/api/src/__tests__/shared.test.ts b/packages/auth-providers/dbAuth/api/src/__tests__/shared.test.ts index d3bafbea8c39..094a59e83dc3 100644 --- a/packages/auth-providers/dbAuth/api/src/__tests__/shared.test.ts +++ b/packages/auth-providers/dbAuth/api/src/__tests__/shared.test.ts @@ -2,6 +2,7 @@ import crypto from 'node:crypto' import path from 'node:path' import type { APIGatewayProxyEvent } from 'aws-lambda' +import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest' import * as error from '../errors' import { @@ -303,9 +304,7 @@ describe('session cookie extraction', () => { expect(extractCookie(event)).toBeUndefined() }) - // @TODO: Disabled Studio Auth Implementation - // we need to avoid using body instead of headers - it.skip('extracts GraphiQL cookie from the header extensions', () => { + it('extracts GraphiQL cookie from the body extensions', () => { const dbUserId = 42 const cookie = encryptToCookie(JSON.stringify({ id: dbUserId })) @@ -322,33 +321,46 @@ describe('session cookie extraction', () => { expect(extractCookie(event)).toEqual(cookie) }) - // @TODO: Disabled Studio Auth Implementation - // we need to avoid using body instead of headers - it.skip('overwrites cookie with event header GraphiQL when in dev', () => { - const sessionCookie = encryptToCookie( - JSON.stringify({ id: 9999999999 }) + ';' + 'token' + it('extracts GraphiQL cookie from the rw-studio header (Fetch request)', () => { + const dbUserId = 42 + + const impersonatedCookie = encryptToCookie( + JSON.stringify({ id: dbUserId }) ) - event = { + const req = new Request('http://localhost:8910/_rw_mw', { + method: 'POST', headers: { - cookie: sessionCookie, + 'auth-provider': 'dbAuth', + 'rw-studio-impersonation-cookie': impersonatedCookie, + authorization: 'Bearer ' + dbUserId, }, - } + }) + + expect(extractCookie(req)).toEqual(impersonatedCookie) + }) + + it('impersonation cookie takes precendence', () => { + const sessionCookie = encryptToCookie( + JSON.stringify({ id: 9999999999 }) + ';' + 'token' + ) const dbUserId = 42 - const cookie = encryptToCookie(JSON.stringify({ id: dbUserId })) - event.body = JSON.stringify({ - extensions: { - headers: { - 'auth-provider': 'dbAuth', - cookie, - authorization: 'Bearer ' + dbUserId, - }, + const impersonatedCookie = encryptToCookie( + JSON.stringify({ id: dbUserId }) + ) + + event = { + headers: { + cookie: sessionCookie, // This user doesn't exist + 'auth-provider': 'dbAuth', + 'rw-studio-impersonation-cookie': impersonatedCookie, + authorization: 'Bearer ' + dbUserId, }, - }) + } - expect(extractCookie(event)).toEqual(cookie) + expect(extractCookie(event)).toEqual(impersonatedCookie) }) }) }) diff --git a/packages/auth-providers/dbAuth/api/src/decoder.ts b/packages/auth-providers/dbAuth/api/src/decoder.ts index 8f47c0f04363..d479f64025b8 100644 --- a/packages/auth-providers/dbAuth/api/src/decoder.ts +++ b/packages/auth-providers/dbAuth/api/src/decoder.ts @@ -13,7 +13,6 @@ export const createAuthDecoder = (cookieNameOption: string): Decoder => { const session = dbAuthSession(req.event, cookieNameOption) // We no longer compare the session id with the bearer token - // Because we only pass around the encrypted session (in both cookie and header) return session } } @@ -22,7 +21,7 @@ export const createAuthDecoder = (cookieNameOption: string): Decoder => { export const authDecoder: Decoder = async ( _authHeaderValue: string, type: string, - req: { event: APIGatewayProxyEvent } + req: { event: APIGatewayProxyEvent | Request } ) => { if (type !== 'dbAuth') { return null diff --git a/packages/auth-providers/dbAuth/api/src/shared.ts b/packages/auth-providers/dbAuth/api/src/shared.ts index 99e889f99075..2ef2dc00cd96 100644 --- a/packages/auth-providers/dbAuth/api/src/shared.ts +++ b/packages/auth-providers/dbAuth/api/src/shared.ts @@ -2,7 +2,7 @@ import crypto from 'node:crypto' import type { APIGatewayProxyEvent } from 'aws-lambda' -import { getEventHeader } from '@redwoodjs/api' +import { getEventHeader, isFetchApiRequest } from '@redwoodjs/api' import { getConfig, getConfigPath } from '@redwoodjs/project-config' import * as DbAuthError from './errors' @@ -36,9 +36,38 @@ const getPort = () => { return getConfig(configPath).api.port } -// @TODO: reimplement eventGraphiQLHeadersCookie -// Needs a re-implementation on the studio side, because using -// body to send Auth headers requires this function to be async +// When in development environment, check for auth impersonation cookie +// if user has generated graphiql headers +const eventGraphiQLHeadersCookie = (event: APIGatewayProxyEvent | Request) => { + if (process.env.NODE_ENV === 'development') { + const impersationationHeader = getEventHeader( + event, + 'rw-studio-impersonation-cookie' + ) + + if (impersationationHeader) { + return impersationationHeader + } + + // TODO: Remove code below when we remove the old way of passing the cookie + // from Studio, and decide it's OK to break compatibility with older Studio + // versions + try { + if (!isFetchApiRequest(event)) { + const jsonBody = JSON.parse(event.body ?? '{}') + return ( + jsonBody?.extensions?.headers?.cookie || + jsonBody?.extensions?.headers?.Cookie + ) + } + } catch { + // sometimes the event body isn't json + return + } + } + + return +} // decrypts session text using old CryptoJS algorithm (using node:crypto library) const legacyDecryptSession = (encryptedText: string) => { @@ -65,11 +94,7 @@ const legacyDecryptSession = (encryptedText: string) => { // Extracts the session cookie from an event, handling both // development environment GraphiQL headers and production environment headers. export const extractCookie = (event: APIGatewayProxyEvent | Request) => { - // @TODO Disabling Studio Auth impersonation: it uses body instead of headers - // this feels a bit off, but also requires the parsing to become async - - // return eventGraphiQLHeadersCookie(event) || eventHeadersCookie(event) - return getEventHeader(event, 'Cookie') + return eventGraphiQLHeadersCookie(event) || getEventHeader(event, 'Cookie') } function extractEncryptedSessionFromHeader( @@ -191,7 +216,7 @@ export const webAuthnSession = (event: APIGatewayProxyEvent | Request) => { return null } - const webAuthnCookie = cookieHeader.split(';').find((cook) => { + const webAuthnCookie = cookieHeader.split(';').find((cook: string) => { return cook.split('=')[0].trim() === 'webAuthn' }) diff --git a/packages/auth-providers/dbAuth/api/vitest.config.mts b/packages/auth-providers/dbAuth/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/dbAuth/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/dbAuth/setup/jest.config.js b/packages/auth-providers/dbAuth/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/dbAuth/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/dbAuth/setup/package.json b/packages/auth-providers/dbAuth/setup/package.json index a12106f6d30c..a80a2e321fae 100644 --- a/packages/auth-providers/dbAuth/setup/package.json +++ b/packages/auth-providers/dbAuth/setup/package.json @@ -18,9 +18,7 @@ "build:pack": "yarn pack -o redwoodjs-auth-dbauth-setup.tgz", "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", - "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src --passWithNoTests", - "test:watch": "yarn test --watch" + "prepublishOnly": "NODE_ENV=production yarn build" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -35,7 +33,6 @@ "@babel/core": "^7.22.20", "@simplewebauthn/typescript-types": "7.4.0", "@types/yargs": "17.0.32", - "jest": "29.7.0", "typescript": "5.3.3" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" diff --git a/packages/auth-providers/firebase/api/jest.config.js b/packages/auth-providers/firebase/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/firebase/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/firebase/api/package.json b/packages/auth-providers/firebase/api/package.json index e90b4cb93bb4..45841bf6e9a1 100644 --- a/packages/auth-providers/firebase/api/package.json +++ b/packages/auth-providers/firebase/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -32,8 +32,8 @@ "@babel/core": "^7.22.20", "@redwoodjs/api": "6.0.7", "@types/aws-lambda": "8.10.126", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/firebase/api/src/__tests__/firebase.test.ts b/packages/auth-providers/firebase/api/src/__tests__/firebase.test.ts index f65556edf46e..a7267c2b0e29 100644 --- a/packages/auth-providers/firebase/api/src/__tests__/firebase.test.ts +++ b/packages/auth-providers/firebase/api/src/__tests__/firebase.test.ts @@ -1,11 +1,12 @@ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda' import admin from 'firebase-admin' +import { vi, test, expect } from 'vitest' import { authDecoder } from '../decoder' -const verifyIdToken = jest.fn() +const verifyIdToken = vi.fn() -jest.spyOn(admin, 'auth').mockImplementation((() => { +vi.spyOn(admin, 'auth').mockImplementation((() => { return { verifyIdToken, } diff --git a/packages/auth-providers/firebase/api/vitest.config.mts b/packages/auth-providers/firebase/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/firebase/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/firebase/setup/jest.config.js b/packages/auth-providers/firebase/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/firebase/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/firebase/setup/package.json b/packages/auth-providers/firebase/setup/package.json index 7b341fe7a0ff..c0d8c2713fe0 100644 --- a/packages/auth-providers/firebase/setup/package.json +++ b/packages/auth-providers/firebase/setup/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,8 +31,8 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/firebase/setup/src/__tests__/setup.test.ts b/packages/auth-providers/firebase/setup/src/__tests__/setup.test.ts index b83698bd800a..6a5cb4dc2a22 100644 --- a/packages/auth-providers/firebase/setup/src/__tests__/setup.test.ts +++ b/packages/auth-providers/firebase/setup/src/__tests__/setup.test.ts @@ -1,10 +1,12 @@ +import { vi, test, expect } from 'vitest' + import { command, description, builder, handler } from '../setup' // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { - errorTelemetry: () => jest.fn(), - timedTelemetry: () => jest.fn(), + errorTelemetry: () => vi.fn(), + timedTelemetry: () => vi.fn(), } }) diff --git a/packages/auth-providers/firebase/setup/vitest.config.mts b/packages/auth-providers/firebase/setup/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/firebase/setup/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/netlify/api/jest.config.js b/packages/auth-providers/netlify/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/netlify/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/netlify/api/package.json b/packages/auth-providers/netlify/api/package.json index 80246e749102..6b860fd80dce 100644 --- a/packages/auth-providers/netlify/api/package.json +++ b/packages/auth-providers/netlify/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -33,8 +33,8 @@ "@redwoodjs/api": "6.0.7", "@types/aws-lambda": "8.10.126", "@types/jsonwebtoken": "9.0.5", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/netlify/api/src/__tests__/netlify.test.ts b/packages/auth-providers/netlify/api/src/__tests__/netlify.test.ts index d47cacb985dd..cb4cebf3ee8e 100644 --- a/packages/auth-providers/netlify/api/src/__tests__/netlify.test.ts +++ b/packages/auth-providers/netlify/api/src/__tests__/netlify.test.ts @@ -1,22 +1,27 @@ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda' import jwt from 'jsonwebtoken' +import { vi, beforeAll, afterAll, test, expect } from 'vitest' import { authDecoder } from '../decoder' -jest.mock('jsonwebtoken', () => { - const jsonwebtoken = jest.requireActual('jsonwebtoken') +vi.mock('jsonwebtoken', async (importOriginal) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const originalJWT = await importOriginal() return { - ...jsonwebtoken, - verify: jest.fn(), - decode: jest.fn((token: string) => { - const exp = - token === 'expired-token' - ? Math.floor(Date.now() / 1000) - 3600 - : Math.floor(Date.now() / 1000) + 3600 - - return { exp, sub: 'abc123' } - }), + ...originalJWT, + default: { + verify: vi.fn(), + decode: vi.fn((token: string) => { + const exp = + token === 'expired-token' + ? Math.floor(Date.now() / 1000) - 3600 + : Math.floor(Date.now() / 1000) + 3600 + + return { exp, sub: 'abc123' } + }), + }, + TokenExpiredError: originalJWT.TokenExpiredError, } }) diff --git a/packages/auth-providers/netlify/api/vitest.config.mts b/packages/auth-providers/netlify/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/netlify/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/netlify/setup/jest.config.js b/packages/auth-providers/netlify/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/netlify/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/netlify/setup/package.json b/packages/auth-providers/netlify/setup/package.json index 39ba5ebc3504..ccca83faf34a 100644 --- a/packages/auth-providers/netlify/setup/package.json +++ b/packages/auth-providers/netlify/setup/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,8 +31,8 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/netlify/setup/src/__tests__/setup.test.ts b/packages/auth-providers/netlify/setup/src/__tests__/setup.test.ts index 5e33db38575a..9cf0b13b8fd0 100644 --- a/packages/auth-providers/netlify/setup/src/__tests__/setup.test.ts +++ b/packages/auth-providers/netlify/setup/src/__tests__/setup.test.ts @@ -1,10 +1,12 @@ +import { vi, test, expect } from 'vitest' + import { command, description, builder, handler } from '../setup' // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { - errorTelemetry: () => jest.fn(), - timedTelemetry: () => jest.fn(), + errorTelemetry: () => vi.fn(), + timedTelemetry: () => vi.fn(), } }) diff --git a/packages/auth-providers/netlify/setup/vitest.config.mts b/packages/auth-providers/netlify/setup/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/netlify/setup/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/netlify/web/jest.config.js b/packages/auth-providers/netlify/web/jest.config.js deleted file mode 100644 index e691bb8f6dbd..000000000000 --- a/packages/auth-providers/netlify/web/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/netlify/web/package.json b/packages/auth-providers/netlify/web/package.json index 3b48f28847e7..a3ef51f526fc 100644 --- a/packages/auth-providers/netlify/web/package.json +++ b/packages/auth-providers/netlify/web/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -32,9 +32,9 @@ "@babel/core": "^7.22.20", "@types/netlify-identity-widget": "1.9.6", "@types/react": "18.2.37", - "jest": "29.7.0", "react": "0.0.0-experimental-e5205658f-20230913", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "netlify-identity-widget": "1.9.2" diff --git a/packages/auth-providers/netlify/web/src/__tests__/netlify.test.tsx b/packages/auth-providers/netlify/web/src/__tests__/netlify.test.tsx index f6d3feeb905b..f8b692d5f761 100644 --- a/packages/auth-providers/netlify/web/src/__tests__/netlify.test.tsx +++ b/packages/auth-providers/netlify/web/src/__tests__/netlify.test.tsx @@ -1,5 +1,6 @@ import { renderHook, act } from '@testing-library/react' import type * as NetlifyIdentityNS from 'netlify-identity-widget' +import { vi, expect, it, beforeAll, beforeEach, describe } from 'vitest' import type { CurrentUser } from '@redwoodjs/auth' @@ -56,7 +57,7 @@ const netlifyIdentityMockClient: Partial = { currentUser: () => loggedInUser || null, } -const fetchMock = jest.fn() +const fetchMock = vi.fn() fetchMock.mockImplementation(async (_url, options) => { const body = options?.body ? JSON.parse(options.body) : {} diff --git a/packages/auth-providers/netlify/web/vitest.config.mts b/packages/auth-providers/netlify/web/vitest.config.mts new file mode 100644 index 000000000000..52ec6b09db8f --- /dev/null +++ b/packages/auth-providers/netlify/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + environment: 'jsdom', + }, +}) diff --git a/packages/auth-providers/supabase/api/jest.config.js b/packages/auth-providers/supabase/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/supabase/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/supabase/api/package.json b/packages/auth-providers/supabase/api/package.json index 6dffb7b0842f..c65687452e68 100644 --- a/packages/auth-providers/supabase/api/package.json +++ b/packages/auth-providers/supabase/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -33,8 +33,8 @@ "@redwoodjs/api": "6.0.7", "@types/aws-lambda": "8.10.126", "@types/jsonwebtoken": "9.0.5", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/supabase/api/src/__tests__/supabase.test.ts b/packages/auth-providers/supabase/api/src/__tests__/supabase.test.ts index dfd872a68836..dc15eef8dc05 100644 --- a/packages/auth-providers/supabase/api/src/__tests__/supabase.test.ts +++ b/packages/auth-providers/supabase/api/src/__tests__/supabase.test.ts @@ -1,16 +1,19 @@ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda' import jwt from 'jsonwebtoken' +import { vi, beforeAll, afterAll, test, expect } from 'vitest' import { authDecoder } from '../decoder' -jest.mock('jsonwebtoken', () => { +vi.mock('jsonwebtoken', () => { return { - verify: jest.fn(() => { - return { - sub: 'abc123', - } - }), - decode: jest.fn(), + default: { + verify: vi.fn(() => { + return { + sub: 'abc123', + } + }), + decode: vi.fn(), + }, } }) diff --git a/packages/auth-providers/supabase/api/vitest.config.mts b/packages/auth-providers/supabase/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/supabase/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/supabase/setup/jest.config.js b/packages/auth-providers/supabase/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/supabase/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/supabase/setup/package.json b/packages/auth-providers/supabase/setup/package.json index 1a699a6d47ef..1d743c41ea48 100644 --- a/packages/auth-providers/supabase/setup/package.json +++ b/packages/auth-providers/supabase/setup/package.json @@ -18,9 +18,7 @@ "build:pack": "yarn pack -o redwoodjs-auth-supabase-setup.tgz", "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", - "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src --passWithNoTests", - "test:watch": "yarn test --watch" + "prepublishOnly": "NODE_ENV=production yarn build" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,7 +29,6 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", "typescript": "5.3.3" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" diff --git a/packages/auth-providers/supabase/web/jest.config.js b/packages/auth-providers/supabase/web/jest.config.js deleted file mode 100644 index e691bb8f6dbd..000000000000 --- a/packages/auth-providers/supabase/web/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/supabase/web/package.json b/packages/auth-providers/supabase/web/package.json index f0e9076d49c0..c5df68d883f1 100644 --- a/packages/auth-providers/supabase/web/package.json +++ b/packages/auth-providers/supabase/web/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,9 +31,9 @@ "@babel/core": "^7.22.20", "@supabase/supabase-js": "2.39.0", "@types/react": "18.2.37", - "jest": "29.7.0", "react": "0.0.0-experimental-e5205658f-20230913", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "@supabase/supabase-js": "2.39.0" diff --git a/packages/auth-providers/supabase/web/src/__tests__/supabase.test.tsx b/packages/auth-providers/supabase/web/src/__tests__/supabase.test.tsx index a9d79a252cd7..096e3692307d 100644 --- a/packages/auth-providers/supabase/web/src/__tests__/supabase.test.tsx +++ b/packages/auth-providers/supabase/web/src/__tests__/supabase.test.tsx @@ -14,6 +14,7 @@ import type { } from '@supabase/supabase-js' import { AuthError } from '@supabase/supabase-js' import { renderHook, act } from '@testing-library/react' +import { vi, it, describe, beforeAll, beforeEach, expect } from 'vitest' import type { CurrentUser } from '@redwoodjs/auth' @@ -250,7 +251,7 @@ const supabaseMockClient: Partial = { auth: mockSupabaseAuthClient as SupabaseClient['auth'], } -const fetchMock = jest.fn() +const fetchMock = vi.fn() fetchMock.mockImplementation(async (_url, options) => { const body = options?.body ? JSON.parse(options.body) : {} diff --git a/packages/auth-providers/supabase/web/vitest.config.mts b/packages/auth-providers/supabase/web/vitest.config.mts new file mode 100644 index 000000000000..52ec6b09db8f --- /dev/null +++ b/packages/auth-providers/supabase/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + environment: 'jsdom', + }, +}) diff --git a/packages/auth-providers/supertokens/api/jest.config.js b/packages/auth-providers/supertokens/api/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/supertokens/api/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/supertokens/api/package.json b/packages/auth-providers/supertokens/api/package.json index 6a3ebd3dadd9..4c961275eb6c 100644 --- a/packages/auth-providers/supertokens/api/package.json +++ b/packages/auth-providers/supertokens/api/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -33,8 +33,8 @@ "@babel/core": "^7.22.20", "@redwoodjs/api": "6.0.7", "@types/jsonwebtoken": "9.0.5", - "jest": "29.7.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "supertokens-node": "15.2.1" diff --git a/packages/auth-providers/supertokens/api/src/__tests__/supertokens.test.ts b/packages/auth-providers/supertokens/api/src/__tests__/supertokens.test.ts index be12ae1a8b1f..d30873686b08 100644 --- a/packages/auth-providers/supertokens/api/src/__tests__/supertokens.test.ts +++ b/packages/auth-providers/supertokens/api/src/__tests__/supertokens.test.ts @@ -1,12 +1,15 @@ import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda' import jwt from 'jsonwebtoken' +import { vi, beforeAll, afterAll, test, expect } from 'vitest' import { authDecoder } from '../decoder' -jest.mock('jsonwebtoken', () => { +vi.mock('jsonwebtoken', () => { return { - verify: jest.fn(), - decode: jest.fn(), + default: { + verify: vi.fn(), + decode: vi.fn(), + }, } }) diff --git a/packages/auth-providers/supertokens/api/vitest.config.mts b/packages/auth-providers/supertokens/api/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/supertokens/api/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/supertokens/setup/__mocks__/fs.js b/packages/auth-providers/supertokens/setup/__mocks__/fs.js deleted file mode 100644 index 99fec09d82ed..000000000000 --- a/packages/auth-providers/supertokens/setup/__mocks__/fs.js +++ /dev/null @@ -1,220 +0,0 @@ -import path from 'path' - -const fs = { - ...jest.requireActual('fs'), -} - -let mockFiles = {} - -const pathSeparator = path.sep - -const getParentDir = (path) => { - return path.substring(0, path.lastIndexOf(pathSeparator)) -} - -const makeParentDirs = (path) => { - const parentDir = getParentDir(path) - if (parentDir && !(parentDir in mockFiles)) { - mockFiles[parentDir] = undefined - makeParentDirs(parentDir) - } -} - -/** - * This is a custom function that our tests can use during setup to specify - * what the files on the "mock" filesystem should look like when any of the - * `fs` APIs are used. - * - * Sets the state of the mocked file system - * @param newMockFiles - {[filepath]: contents} - */ -fs.__setMockFiles = (newMockFiles) => { - mockFiles = { ...newMockFiles } - - // Generate all the directories which implicitly exist - Object.keys(mockFiles).forEach((mockPath) => { - if (mockPath.includes(pathSeparator)) { - makeParentDirs(mockPath) - } - }) -} - -fs.__getMockFiles = () => { - return mockFiles -} - -fs.readFileSync = (path) => { - // In prisma v4.3.0, prisma format uses a Wasm module. See https://github.com/prisma/prisma/releases/tag/4.3.0. - // We shouldn't mock this, so we'll use the real fs.readFileSync. - if (path.includes('prisma_fmt_build_bg.wasm')) { - return jest.requireActual('fs').readFileSync(path) - } - - if (path in mockFiles) { - return mockFiles[path] - } else { - const fakeError = new Error( - `Error: ENOENT: no such file or directory, open '${path}'` - ) - fakeError.errno = -2 - fakeError.syscall = 'open' - fakeError.code = 'ENOENT' - fakeError.path = path - throw fakeError - } -} - -fs.writeFileSync = (path, contents) => { - const parentDir = getParentDir(path) - if (parentDir && !fs.existsSync(parentDir)) { - const fakeError = new Error( - `Error: ENOENT: no such file or directory, open '${path}'` - ) - fakeError.errno = -2 - fakeError.syscall = 'open' - fakeError.code = 'ENOENT' - fakeError.path = path - throw fakeError - } - mockFiles[path] = contents -} - -fs.appendFileSync = (path, contents) => { - if (path in mockFiles) { - mockFiles[path] = mockFiles[path] + contents - } else { - fs.writeFileSync(path, contents) - } -} - -fs.rmSync = (path, options = {}) => { - if (fs.existsSync(path)) { - if (options.recursive) { - Object.keys(mockFiles).forEach((mockedPath) => { - if (mockedPath.startsWith(path)) { - delete mockFiles[mockedPath] - } - }) - } else { - if (mockFiles[path] === undefined) { - const children = fs.readdirSync(path) - if (children.length !== 0) { - const fakeError = new Error( - `NodeError [SystemError]: Path is a directory: rm returned EISDIR (is a directory) ${path}` - ) - fakeError.errno = 21 - fakeError.syscall = 'rm' - fakeError.code = 'ERR_FS_EISDIR' - fakeError.path = path - throw fakeError - } - } - delete mockFiles[path] - } - } else { - const fakeError = new Error( - `Error: ENOENT: no such file or directory, stat '${path}'` - ) - fakeError.errno = -2 - fakeError.syscall = 'stat' - fakeError.code = 'ENOENT' - fakeError.path = path - throw fakeError - } -} - -fs.unlinkSync = (path) => { - if (path in mockFiles) { - delete mockFiles[path] - } else { - const fakeError = new Error( - `Error: ENOENT: no such file or directory, stat '${path}'` - ) - fakeError.errno = -2 - fakeError.syscall = 'unlink' - fakeError.code = 'ENOENT' - fakeError.path = path - throw fakeError - } -} - -fs.existsSync = (path) => { - return path in mockFiles -} - -fs.copyFileSync = (src, dist) => { - fs.writeFileSync(dist, fs.readFileSync(src)) -} - -fs.readdirSync = (path) => { - if (!fs.existsSync(path)) { - const fakeError = new Error( - `Error: ENOENT: no such file or directory, scandir '${path}'` - ) - fakeError.errno = -2 - fakeError.syscall = 'scandir' - fakeError.code = 'ENOENT' - fakeError.path = path - throw fakeError - } - - if (mockFiles[path] !== undefined) { - const fakeError = new Error( - `Error: ENOTDIR: not a directory, scandir '${path}'` - ) - fakeError.errno = -20 - fakeError.syscall = 'scandir' - fakeError.code = 'ENOTDIR' - fakeError.path = path - throw fakeError - } - - const content = [] - Object.keys(mockFiles).forEach((mockedPath) => { - const childPath = mockedPath.substring(path.length + 1) - if ( - mockedPath.startsWith(path) && - !childPath.includes(pathSeparator) && - childPath - ) { - content.push(childPath) - } - }) - return content -} - -fs.mkdirSync = (path, options = {}) => { - if (options.recursive) { - makeParentDirs(path) - } - // Directories are represented as paths with an "undefined" value - fs.writeFileSync(path, undefined) -} - -fs.rmdirSync = (path, options = {}) => { - if (!fs.existsSync(path)) { - const fakeError = new Error( - `Error: ENOENT: no such file or directory, rmdir '${path}'` - ) - fakeError.errno = -2 - fakeError.syscall = 'rmdir' - fakeError.code = 'ENOENT' - fakeError.path = path - throw fakeError - } - - if (mockFiles[path] !== undefined) { - const fakeError = new Error( - `Error: ENOTDIR: not a directory, rmdir '${path}'` - ) - fakeError.errno = -20 - fakeError.syscall = 'rmdir' - fakeError.code = 'ENOTDIR' - fakeError.path = path - throw fakeError - } - - fs.rmSync(path, options) -} - -module.exports = fs diff --git a/packages/auth-providers/supertokens/setup/jest.config.js b/packages/auth-providers/supertokens/setup/jest.config.js deleted file mode 100644 index dee127c25474..000000000000 --- a/packages/auth-providers/supertokens/setup/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/supertokens/setup/package.json b/packages/auth-providers/supertokens/setup/package.json index c44702cc29c1..d85a20039801 100644 --- a/packages/auth-providers/supertokens/setup/package.json +++ b/packages/auth-providers/supertokens/setup/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src --passWithNoTests", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,8 +31,9 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/yargs": "17.0.32", - "jest": "29.7.0", - "typescript": "5.3.3" + "memfs": "4.6.0", + "typescript": "5.3.3", + "vitest": "1.2.1" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/auth-providers/supertokens/setup/src/__tests__/setup.test.ts b/packages/auth-providers/supertokens/setup/src/__tests__/setup.test.ts index 7c108e1d2eb8..32d8a9fc0cac 100644 --- a/packages/auth-providers/supertokens/setup/src/__tests__/setup.test.ts +++ b/packages/auth-providers/supertokens/setup/src/__tests__/setup.test.ts @@ -1,10 +1,12 @@ +import { vi, test, expect } from 'vitest' + import { command, description, builder, handler } from '../setup' // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { - errorTelemetry: () => jest.fn(), - timedTelemetry: () => jest.fn(), + errorTelemetry: () => vi.fn(), + timedTelemetry: () => vi.fn(), } }) diff --git a/packages/auth-providers/supertokens/setup/src/__tests__/setupHandler.test.ts b/packages/auth-providers/supertokens/setup/src/__tests__/setupHandler.test.ts index 6789d79d940c..a19692eee4e4 100644 --- a/packages/auth-providers/supertokens/setup/src/__tests__/setupHandler.test.ts +++ b/packages/auth-providers/supertokens/setup/src/__tests__/setupHandler.test.ts @@ -1,12 +1,12 @@ // mock Telemetry for CLI commands so they don't try to spawn a process -jest.mock('@redwoodjs/telemetry', () => { +vi.mock('@redwoodjs/telemetry', () => { return { - errorTelemetry: () => jest.fn(), - timedTelemetry: () => jest.fn(), + errorTelemetry: () => vi.fn(), + timedTelemetry: () => vi.fn(), } }) -jest.mock('@redwoodjs/cli-helpers', () => { +vi.mock('@redwoodjs/cli-helpers', () => { return { getPaths: () => { return { @@ -18,24 +18,22 @@ jest.mock('@redwoodjs/cli-helpers', () => { base: '', } }, - standardAuthHandler: () => jest.fn(), + standardAuthHandler: () => vi.fn(), } }) -// This will load packages/auth-providers/supertokens/setup/__mocks__/fs.js -jest.mock('fs') - -const mockFS = fs as unknown as Omit, 'readdirSync'> & { - __setMockFiles: (files: Record) => void -} +vi.mock('fs', async () => ({ default: (await import('memfs')).fs })) import fs from 'fs' +import { vol } from 'memfs' +import { vi, describe, it, expect } from 'vitest' + import { addRoutingLogic } from '../setupHandler' describe('addRoutingLogic', () => { it('modifies the Routes.{jsx,tsx} file', () => { - mockFS.__setMockFiles({ + vol.fromJSON({ 'Routes.tsx': "// In this file, all Page components from 'src/pages' are auto-imported.\n" + ` @@ -59,7 +57,7 @@ export default Routes addRoutingLogic.task() - expect(mockFS.readFileSync('Routes.tsx')).toMatchInlineSnapshot(` + expect(fs.readFileSync('Routes.tsx', 'utf-8')).toMatchInlineSnapshot(` "// In this file, all Page components from 'src/pages' are auto-imported. import { canHandleRoute, getRoutingComponent } from 'supertokens-auth-react/ui' @@ -88,7 +86,7 @@ export default Routes }) it('handles a Routes.{jsx,tsx} file with a legacy setup', () => { - mockFS.__setMockFiles({ + vol.fromJSON({ 'Routes.tsx': "// In this file, all Page components from 'src/pages' are auto-imported.\n" + ` @@ -118,7 +116,7 @@ export default Routes addRoutingLogic.task() - expect(mockFS.readFileSync('Routes.tsx')).toMatchInlineSnapshot(` + expect(fs.readFileSync('Routes.tsx', 'utf-8')).toMatchInlineSnapshot(` "// In this file, all Page components from 'src/pages' are auto-imported. @@ -134,7 +132,7 @@ export default Routes return getRoutingComponent(PreBuiltUI) } - + return ( diff --git a/packages/auth-providers/supertokens/setup/vitest.config.mts b/packages/auth-providers/supertokens/setup/vitest.config.mts new file mode 100644 index 000000000000..4c8730d4808b --- /dev/null +++ b/packages/auth-providers/supertokens/setup/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + }, +}) diff --git a/packages/auth-providers/supertokens/web/jest.config.js b/packages/auth-providers/supertokens/web/jest.config.js deleted file mode 100644 index e691bb8f6dbd..000000000000 --- a/packages/auth-providers/supertokens/web/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['fixtures', 'dist'], -} diff --git a/packages/auth-providers/supertokens/web/package.json b/packages/auth-providers/supertokens/web/package.json index 423f4ff54c7c..43d43e261621 100644 --- a/packages/auth-providers/supertokens/web/package.json +++ b/packages/auth-providers/supertokens/web/package.json @@ -19,8 +19,8 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest src", - "test:watch": "yarn test --watch" + "test": "vitest run src", + "test:watch": "vitest watch src" }, "dependencies": { "@babel/runtime-corejs3": "7.23.6", @@ -31,10 +31,10 @@ "@babel/cli": "7.23.4", "@babel/core": "^7.22.20", "@types/react": "18.2.37", - "jest": "29.7.0", "react": "0.0.0-experimental-e5205658f-20230913", "supertokens-auth-react": "0.34.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "1.2.1" }, "peerDependencies": { "supertokens-auth-react": "0.34.0" diff --git a/packages/auth-providers/supertokens/web/src/__tests__/supertokens.test.tsx b/packages/auth-providers/supertokens/web/src/__tests__/supertokens.test.tsx index a740b41134e9..d20fdb9df410 100644 --- a/packages/auth-providers/supertokens/web/src/__tests__/supertokens.test.tsx +++ b/packages/auth-providers/supertokens/web/src/__tests__/supertokens.test.tsx @@ -1,4 +1,5 @@ import { renderHook, act } from '@testing-library/react' +import { vi, it, expect, beforeAll, beforeEach, describe } from 'vitest' import type { CurrentUser } from '@redwoodjs/auth' @@ -49,7 +50,7 @@ const superTokensMockClient: SuperTokensAuth = { }, } -const fetchMock = jest.fn() +const fetchMock = vi.fn() fetchMock.mockImplementation(async (_url, options) => { const body = options?.body ? JSON.parse(options.body) : {} diff --git a/packages/auth-providers/supertokens/web/vitest.config.mts b/packages/auth-providers/supertokens/web/vitest.config.mts new file mode 100644 index 000000000000..52ec6b09db8f --- /dev/null +++ b/packages/auth-providers/supertokens/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/fixtures'], + environment: 'jsdom', + }, +}) diff --git a/packages/graphql-server/src/functions/__tests__/normalizeRequest.test.ts b/packages/graphql-server/src/functions/__tests__/normalizeRequest.test.ts deleted file mode 100644 index 3f9590dbae46..000000000000 --- a/packages/graphql-server/src/functions/__tests__/normalizeRequest.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Headers } from '@whatwg-node/fetch' -import type { APIGatewayProxyEvent } from 'aws-lambda' - -import { normalizeRequest } from '@redwoodjs/api' - -export const createMockedEvent = ( - httpMethod = 'POST', - body: any = undefined, - isBase64Encoded = false -): APIGatewayProxyEvent => { - return { - body, - headers: {}, - multiValueHeaders: {}, - httpMethod, - isBase64Encoded, - path: '/MOCK_PATH', - pathParameters: null, - queryStringParameters: null, - multiValueQueryStringParameters: null, - stageVariables: null, - requestContext: { - accountId: 'MOCKED_ACCOUNT', - apiId: 'MOCKED_API_ID', - authorizer: { name: 'MOCKED_AUTHORIZER' }, - protocol: 'HTTP', - identity: { - accessKey: null, - accountId: null, - apiKey: null, - apiKeyId: null, - caller: null, - clientCert: null, - cognitoAuthenticationProvider: null, - cognitoAuthenticationType: null, - cognitoIdentityId: null, - cognitoIdentityPoolId: null, - principalOrgId: null, - sourceIp: '123.123.123.123', - user: null, - userAgent: null, - userArn: null, - }, - httpMethod: 'POST', - path: '/MOCK_PATH', - stage: 'MOCK_STAGE', - requestId: 'MOCKED_REQUEST_ID', - requestTimeEpoch: 1, - resourceId: 'MOCKED_RESOURCE_ID', - resourcePath: 'MOCKED_RESOURCE_PATH', - }, - resource: 'MOCKED_RESOURCE', - } -} - -test('Normalizes an aws event with base64', () => { - const corsEventB64 = createMockedEvent( - 'POST', - Buffer.from(JSON.stringify({ bazinga: 'hello_world' }), 'utf8').toString( - 'base64' - ), - true - ) - - const normalizedRequest = normalizeRequest(corsEventB64) - const expectedRequest = { - headers: new Headers(corsEventB64.headers), - method: 'POST', - query: null, - body: { - bazinga: 'hello_world', - }, - } - - expect(normalizedRequest.method).toEqual(expectedRequest.method) - expect(normalizedRequest.query).toEqual(expectedRequest.query) - expect(normalizedRequest.body).toEqual(expectedRequest.body) - expectedRequest.headers.forEach((value, key) => { - expect(normalizedRequest.headers.get(key)).toEqual(value) - }) -}) - -test('Handles CORS requests with and without b64 encoded', () => { - const corsEventB64 = createMockedEvent('OPTIONS', undefined, true) - - const normalizedRequest = normalizeRequest(corsEventB64) - const expectedRequest = { - headers: new Headers(corsEventB64.headers), - method: 'OPTIONS', - query: null, - body: undefined, - } - expect(normalizedRequest.method).toEqual(expectedRequest.method) - expect(normalizedRequest.query).toEqual(expectedRequest.query) - expect(normalizedRequest.body).toEqual(expectedRequest.body) - expectedRequest.headers.forEach((value, key) => { - expect(normalizedRequest.headers.get(key)).toEqual(value) - }) -}) diff --git a/packages/project-config/build.js b/packages/project-config/build.js new file mode 100644 index 000000000000..4560f1d78590 --- /dev/null +++ b/packages/project-config/build.js @@ -0,0 +1,28 @@ +/* eslint-disable import/no-extraneous-dependencies */ + +import * as esbuild from 'esbuild' + +const options = { + entryPoints: ['./src/index.ts'], + outdir: 'dist', + + platform: 'node', + target: ['node20'], + bundle: true, + packages: 'external', + + logLevel: 'info', + metafile: true, +} + +await esbuild.build({ + ...options, + format: 'esm', + outExtension: { '.js': '.mjs' }, +}) + +await esbuild.build({ + ...options, + format: 'cjs', + outExtension: { '.js': '.cjs' }, +}) diff --git a/packages/project-config/build.mjs b/packages/project-config/build.mjs deleted file mode 100644 index 2cec19a1a453..000000000000 --- a/packages/project-config/build.mjs +++ /dev/null @@ -1,22 +0,0 @@ -import fs from 'node:fs' - -import * as esbuild from 'esbuild' - -const result = await esbuild.build({ - entryPoints: ['src/index.ts'], - outdir: 'dist', - - bundle: true, - - platform: 'node', - target: ['node20'], - packages: 'external', - - logLevel: 'info', - - // For visualizing the bundle. - // See https://esbuild.github.io/api/#metafile and https://esbuild.github.io/analyze/. - metafile: true, -}) - -fs.writeFileSync('meta.json', JSON.stringify(result.metafile)) diff --git a/packages/project-config/package.json b/packages/project-config/package.json index 6d3e4e692316..2c1e5f34767c 100644 --- a/packages/project-config/package.json +++ b/packages/project-config/package.json @@ -7,15 +7,18 @@ "directory": "packages/project-config" }, "license": "MIT", + "type": "module", "exports": { - ".": "./dist/index.js" + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "default": "./dist/index.cjs" }, "types": "./dist/index.d.ts", "files": [ "dist" ], "scripts": { - "build": "yarn node ./build.mjs && run build:types", + "build": "yarn node ./build.js && run build:types", "build:pack": "yarn pack -o redwoodjs-project-config.tgz", "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,ts,tsx\" --ignore dist --exec \"yarn build\"", diff --git a/packages/project-config/src/config.ts b/packages/project-config/src/config.ts index e8dcdfda4146..4c226ac3ccde 100644 --- a/packages/project-config/src/config.ts +++ b/packages/project-config/src/config.ts @@ -4,7 +4,7 @@ import toml from '@iarna/toml' import merge from 'deepmerge' import { env as envInterpolation } from 'string-env-interpolation' -import { getConfigPath } from './configPath' +import { getConfigPath } from './configPath.js' export enum TargetEnum { NODE = 'node', diff --git a/packages/project-config/src/configPath.ts b/packages/project-config/src/configPath.ts index 697136284f2f..d24e85b039d9 100644 --- a/packages/project-config/src/configPath.ts +++ b/packages/project-config/src/configPath.ts @@ -1,4 +1,4 @@ -import { findUp } from './findUp' +import { findUp } from './findUp.js' const CONFIG_FILE_NAME = 'redwood.toml' diff --git a/packages/project-config/src/index.ts b/packages/project-config/src/index.ts index ae6da932ae6e..e420284a615d 100644 --- a/packages/project-config/src/index.ts +++ b/packages/project-config/src/index.ts @@ -1,4 +1,4 @@ -export * from './config' -export * from './configPath' -export * from './paths' -export * from './findUp' +export * from './config.js' +export * from './configPath.js' +export * from './paths.js' +export * from './findUp.js' diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index ed79fb75db34..62902ef16fa4 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -3,8 +3,8 @@ import path from 'path' import fg from 'fast-glob' -import { getConfig } from './config' -import { getConfigPath } from './configPath' +import { getConfig } from './config.js' +import { getConfigPath } from './configPath.js' export interface NodeTargetPaths { base: string diff --git a/packages/project-config/tsconfig.json b/packages/project-config/tsconfig.json index 74bdeb595478..78a31c197ddd 100644 --- a/packages/project-config/tsconfig.json +++ b/packages/project-config/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "../../tsconfig.compilerOption.json", "compilerOptions": { + "moduleResolution": "NodeNext", + "module": "NodeNext", "baseUrl": ".", "rootDir": "src", "outDir": "dist", diff --git a/packages/vite/bins/rw-vite-dev.mjs b/packages/vite/bins/rw-vite-dev.mjs index ba0e4e5a205a..b136728048b6 100755 --- a/packages/vite/bins/rw-vite-dev.mjs +++ b/packages/vite/bins/rw-vite-dev.mjs @@ -2,9 +2,9 @@ import { createServer } from 'vite' import yargsParser from 'yargs-parser' -import projectConfig from '@redwoodjs/project-config' +import { getPaths } from '@redwoodjs/project-config' -const rwPaths = projectConfig.getPaths() +const rwPaths = getPaths() const startDevServer = async () => { const configFile = rwPaths.web.viteConfig diff --git a/packages/vite/src/__tests__/viteNestedPages.test.mts b/packages/vite/src/__tests__/viteNestedPages.test.mts index ba38be25bbb5..27805d40a1b5 100644 --- a/packages/vite/src/__tests__/viteNestedPages.test.mts +++ b/packages/vite/src/__tests__/viteNestedPages.test.mts @@ -6,7 +6,7 @@ import { transformWithEsbuild } from 'vite' import { test, describe, beforeEach, afterAll, beforeAll, it, expect, vi } from 'vitest' import * as babel from '@babel/core' -import projectConfig from '@redwoodjs/project-config' +import { getPaths } from '@redwoodjs/project-config' import { Flags, @@ -41,7 +41,6 @@ async function transform(srcPath: string) { const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const FIXTURE_PATH = path.join(__dirname, 'fixtures/nestedPages') -const { getPaths } = projectConfig test('transform', async () => { vi.spyOn(fs, 'readFileSync').mockImplementationOnce(() => { diff --git a/yarn.lock b/yarn.lock index 41c9d6b4866e..6f8001bd4a4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7596,10 +7596,10 @@ __metadata: "@redwoodjs/api": "npm:6.0.7" "@types/jsonwebtoken": "npm:9.0.5" core-js: "npm:3.34.0" - jest: "npm:29.7.0" jsonwebtoken: "npm:9.0.2" jwks-rsa: "npm:3.1.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7613,8 +7613,8 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7629,9 +7629,9 @@ __metadata: "@redwoodjs/auth": "npm:6.0.7" "@types/react": "npm:18.2.37" core-js: "npm:3.34.0" - jest: "npm:29.7.0" react: "npm:0.0.0-experimental-e5205658f-20230913" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: "@auth0/auth0-spa-js": 2.1.2 languageName: unknown @@ -7648,10 +7648,10 @@ __metadata: "@types/aws-lambda": "npm:8.10.126" "@types/jsonwebtoken": "npm:9.0.5" core-js: "npm:3.34.0" - jest: "npm:29.7.0" jsonwebtoken: "npm:9.0.2" jwks-rsa: "npm:3.1.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7665,8 +7665,8 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7682,9 +7682,9 @@ __metadata: "@types/netlify-identity-widget": "npm:1.9.6" "@types/react": "npm:18.2.37" core-js: "npm:3.34.0" - jest: "npm:29.7.0" react: "npm:0.0.0-experimental-e5205658f-20230913" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: "@azure/msal-browser": 2.38.3 languageName: unknown @@ -7701,8 +7701,8 @@ __metadata: "@redwoodjs/api": "npm:6.0.7" "@types/aws-lambda": "npm:8.10.126" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7716,7 +7716,6 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" languageName: unknown linkType: soft @@ -7733,9 +7732,9 @@ __metadata: "@redwoodjs/auth": "npm:6.0.7" "@types/react": "npm:18.2.37" core-js: "npm:3.34.0" - jest: "npm:29.7.0" react: "npm:0.0.0-experimental-e5205658f-20230913" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: "@clerk/clerk-react": 4.28.3 languageName: unknown @@ -7751,8 +7750,8 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7770,10 +7769,10 @@ __metadata: "@types/uuid": "npm:9.0.7" base64url: "npm:3.0.1" core-js: "npm:3.34.0" - jest: "npm:29.7.0" md5: "npm:2.3.0" typescript: "npm:5.3.3" uuid: "npm:9.0.1" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7789,7 +7788,6 @@ __metadata: "@simplewebauthn/typescript-types": "npm:7.4.0" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" prompts: "npm:2.4.2" terminal-link: "npm:2.1.1" typescript: "npm:5.3.3" @@ -7825,8 +7823,8 @@ __metadata: "@types/aws-lambda": "npm:8.10.126" core-js: "npm:3.34.0" firebase-admin: "npm:11.11.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7840,8 +7838,8 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7875,9 +7873,9 @@ __metadata: "@types/aws-lambda": "npm:8.10.126" "@types/jsonwebtoken": "npm:9.0.5" core-js: "npm:3.34.0" - jest: "npm:29.7.0" jsonwebtoken: "npm:9.0.2" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7891,8 +7889,8 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7907,9 +7905,9 @@ __metadata: "@types/netlify-identity-widget": "npm:1.9.6" "@types/react": "npm:18.2.37" core-js: "npm:3.34.0" - jest: "npm:29.7.0" react: "npm:0.0.0-experimental-e5205658f-20230913" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: netlify-identity-widget: 1.9.2 languageName: unknown @@ -7926,9 +7924,9 @@ __metadata: "@types/aws-lambda": "npm:8.10.126" "@types/jsonwebtoken": "npm:9.0.5" core-js: "npm:3.34.0" - jest: "npm:29.7.0" jsonwebtoken: "npm:9.0.2" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -7942,7 +7940,6 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" typescript: "npm:5.3.3" languageName: unknown linkType: soft @@ -7957,9 +7954,9 @@ __metadata: "@supabase/supabase-js": "npm:2.39.0" "@types/react": "npm:18.2.37" core-js: "npm:3.34.0" - jest: "npm:29.7.0" react: "npm:0.0.0-experimental-e5205658f-20230913" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: "@supabase/supabase-js": 2.39.0 languageName: unknown @@ -7975,10 +7972,10 @@ __metadata: "@redwoodjs/api": "npm:6.0.7" "@types/jsonwebtoken": "npm:9.0.5" core-js: "npm:3.34.0" - jest: "npm:29.7.0" jsonwebtoken: "npm:9.0.2" jwks-rsa: "npm:3.1.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: supertokens-node: 15.2.1 languageName: unknown @@ -7994,8 +7991,9 @@ __metadata: "@redwoodjs/cli-helpers": "npm:6.0.7" "@types/yargs": "npm:17.0.32" core-js: "npm:3.34.0" - jest: "npm:29.7.0" + memfs: "npm:4.6.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" languageName: unknown linkType: soft @@ -8009,10 +8007,10 @@ __metadata: "@redwoodjs/auth": "npm:6.0.7" "@types/react": "npm:18.2.37" core-js: "npm:3.34.0" - jest: "npm:29.7.0" react: "npm:0.0.0-experimental-e5205658f-20230913" supertokens-auth-react: "npm:0.34.0" typescript: "npm:5.3.3" + vitest: "npm:1.2.1" peerDependencies: supertokens-auth-react: 0.34.0 languageName: unknown