Skip to content

Commit

Permalink
Merge branch 'main' of github.com:redwoodjs/redwood into chore/p2-web…
Browse files Browse the repository at this point in the history
…-esm

* 'main' of github.com:redwoodjs/redwood:
  fix(deps): update dependency prettier to v3.3.2 (redwoodjs#10852)
  fix(deps): update dependency vite to v5.3.1 (redwoodjs#10854)
  chore(esm/cjs): Build rwjs/web to cjs with new build system (redwoodjs#10826)
  feat(dbAuth): Automatically create User model in fresh projects (redwoodjs#10871)
  feat(dbAuth): Prompt to generate dbAuth pages (redwoodjs#10865)
  • Loading branch information
dac09 committed Jun 24, 2024
2 parents 360645c + a3879f3 commit 0459c6c
Show file tree
Hide file tree
Showing 22 changed files with 681 additions and 370 deletions.
3 changes: 3 additions & 0 deletions .changesets/10865.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- feat(dbAuth): Prompt to generate dbAuth pages (#10865) by @Tobbe

When setting up dbAuth we'll now prompt if the user also wants to generate pages for login, signup, password reset etc. We only prompt if no existing pages exist.
4 changes: 4 additions & 0 deletions .changesets/10871.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- feat(dbAuth): Automatically create User model in fresh projects (#10871) by @Tobbe

Automatically create a `User` model in the project's `schema.prisma` when
setting up dbAuth in a new project.
241 changes: 233 additions & 8 deletions packages/auth-providers/dbAuth/setup/src/__tests__/setup.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
import path from 'node:path'

import { vol } from 'memfs'
import prompts from 'prompts'

import { type AuthHandlerArgs } from '@redwoodjs/cli-helpers'

jest.mock('fs', () => require('memfs').fs)

import { createAuthDecoderFunction } from '../setupHandler'
import { createAuthDecoderFunction, handler } from '../setupHandler'

const RWJS_CWD = process.env.RWJS_CWD
const redwoodProjectPath = '/redwood-app'
const mockLoginPagePath = path.join(
redwoodProjectPath,
'web/src/pages/LoginPage/LoginPage.tsx',
)

jest.mock('../setupData', () => ({
notes: '',
extraTask: undefined,
}))
jest.mock('prompts', () => {
return {
__esModule: true,
default: jest.fn(async (args: any) => {
return {
[args.name]: false,
}
}),
}
})

jest.mock('../shared', () => ({
hasModel: () => false,
hasAuthPages: () => {
return require('fs').existsSync(mockLoginPagePath)
},
generateAuthPagesTask: () => undefined,
getModelNames: () => ['ExampleUser'],
}))

jest.mock('@redwoodjs/cli-helpers', () => {
return {
getGraphqlPath: () => {
return redwoodProjectPath + '/api/src/functions/graphql.ts'
},
addEnvVarTask: () => undefined,
getPaths: () => ({
base: redwoodProjectPath,
}),
Expand All @@ -34,6 +53,12 @@ jest.mock('@redwoodjs/cli-helpers', () => {
bold: (str: string) => str,
underline: (str: string) => str,
},
// I wish I could have used something like
// jest.requireActual(@redwoodjs/cli-helpers) here, but I couldn't because
// jest doesn't support ESM
standardAuthHandler: async (args: AuthHandlerArgs) => {
args.notes && console.log(`\n ${args.notes.join('\n ')}\n`)
},
}
})

Expand All @@ -45,13 +70,24 @@ afterAll(() => {
process.env.RWJS_CWD = RWJS_CWD
})

beforeEach(() => {
jest.spyOn(console, 'log').mockImplementation(() => {})
})

afterEach(() => {
jest.mocked(console).log.mockRestore?.()
jest.mocked(prompts).mockClear?.()
})

describe('dbAuth setup command', () => {
it('does not duplicate authDecoder creation', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')
const graphqlTsPath = 'api/src/functions/graphql.ts'

vol.fromJSON(
{
[path.resolve(__dirname, '../../package.json')]:
'{ "version": "0.0.0" }',
'api/src/functions/graphql.ts': `
[packageJsonPath]: '{ "version": "0.0.0" }',
[graphqlTsPath]: `
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
import directives from 'src/directives/**/*.{js,ts}'
Expand Down Expand Up @@ -91,4 +127,193 @@ export const handler = createGraphQLHandler({
vol.toJSON()[redwoodProjectPath + '/api/src/functions/graphql.ts']
expect(updatedGraphqlTs).toEqual(updatedGraphqlTs2)
})

it('prompts to generate pages', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: false,
createUserModel: false,
generateAuthPages: null,
force: false,
})

expect(jest.mocked(prompts).mock.calls[0][0].message).toMatch(
/Generate auth pages/,
)
})

it('does not prompt to generate pages when pages already exist', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
[mockLoginPagePath]: 'export default () => <div>Login</div>',
},
redwoodProjectPath,
)

await handler({
webauthn: false,
createUserModel: false,
generateAuthPages: null,
force: false,
})

expect(jest.mocked(prompts)).not.toHaveBeenCalled()
})

describe('One More Thing... message', () => {
describe('page generation hint', () => {
it('is not included if page generation was already done as part of the setup process', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: false,
createUserModel: false,
generateAuthPages: true,
force: false,
})

expect(jest.mocked(console).log.mock.calls[0][0]).not.toContain(
'yarn rw generate dbAuth',
)
})

it('is not included for WebAuthn if page generation was already done as part of the setup process', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: true,
createUserModel: false,
generateAuthPages: true,
force: false,
})

expect(jest.mocked(console).log.mock.calls[0][0]).not.toContain(
'yarn rw generate dbAuth',
)
})

it('is not included if page generation and model generation was already done as part of the setup process', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: false,
createUserModel: true,
generateAuthPages: true,
force: false,
})

expect(jest.mocked(console).log.mock.calls[0][0]).not.toContain(
'yarn rw generate dbAuth',
)
})

it('is not included for WebAuthn if page generation and model generation was already done as part of the setup process', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: true,
createUserModel: true,
generateAuthPages: true,
force: false,
})

expect(jest.mocked(console).log.mock.calls[0][0]).not.toContain(
'yarn rw generate dbAuth',
)
})

it('is included if page generation was not done as part of the setup process', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: false,
createUserModel: false,
generateAuthPages: false,
force: false,
})

const logs: string[][] = [...jest.mocked(console).log.mock.calls]
const oneMoreThingMessage = logs.find((log) => {
return log[0].includes('Done! But you have a little more work to do')
})?.[0]

// Included exactly once
expect(
oneMoreThingMessage?.match(/yarn rw generate dbAuth/g),
).toHaveLength(1)
})

it('is included for WebAuthn if page generation was not done as part of the setup process', async () => {
const packageJsonPath = path.resolve(__dirname, '../../package.json')

vol.fromJSON(
{
[packageJsonPath]: '{ "version": "0.0.0" }',
},
redwoodProjectPath,
)

await handler({
webauthn: true,
createUserModel: false,
generateAuthPages: false,
force: false,
})

const firstLogMessage = jest.mocked(console).log.mock.calls[0][0]

// Included exactly once
expect(firstLogMessage.match(/and WebAuthn prompts/g)).toHaveLength(1)
expect(firstLogMessage.match(/yarn rw generate dbAuth/g)).toHaveLength(
1,
)
})
})
})
})
Loading

0 comments on commit 0459c6c

Please sign in to comment.