Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): Allow common custom scaffold template files #11755

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6569464
feat(cli): allow common custom component files to be generated by sca…
esteban-url Dec 7, 2024
bce7a71
feat(cli): allow common custom page files to be generated by scaffold…
esteban-url Dec 7, 2024
3cd47bf
chore: add changeset
esteban-url Dec 7, 2024
589d855
Merge branch 'main' into er-custom-scaffold-files
esteban-url Dec 9, 2024
ee9df89
Merge branch 'main' into er-custom-scaffold-files
esteban-url Dec 26, 2024
8b1585e
Merge branch 'main' into er-custom-scaffold-files
Tobbe Dec 27, 2024
ecf9f0e
more specific regex
Tobbe Dec 27, 2024
b633c6b
add tests for mocks, tests etc
Tobbe Dec 27, 2024
24799b0
Merge branch 'main' into er-custom-scaffold-files
esteban-url Dec 28, 2024
776518e
feat(scaffold): add mock templates and test files for new components
esteban-url Dec 29, 2024
51f83ad
fix(scaffold): remove duplicated template files
esteban-url Dec 30, 2024
0fc3c9b
fix(scaffold): correct typo in command name from 'destory' to 'destroy'
esteban-url Dec 30, 2024
a853a9f
test(scaffold): update tests to expect 48 files instead of 19
esteban-url Dec 30, 2024
4263d32
feat(scaffold): add mock & test templates for all pages & components …
esteban-url Dec 30, 2024
ce10dcf
refactor(scaffold): restore default functionality and update tests
esteban-url Dec 30, 2024
be06b9c
chore(changelog): update changelog description
esteban-url Dec 30, 2024
692e21c
Merge branch 'main' into er-custom-scaffold-files
esteban-url Dec 30, 2024
d11aa8f
test() instead of match()
Tobbe Jan 1, 2025
cf9d91a
test-project update
Tobbe Jan 1, 2025
1f16d1b
consistent mock values
Tobbe Jan 2, 2025
af40618
feat: showcase the use of relations
esteban-url Jan 3, 2025
21278a5
update snapshots
esteban-url Jan 3, 2025
393068e
Update test project
Tobbe Jan 6, 2025
43773c0
Merge branch 'main' into er-custom-scaffold-files
Tobbe Jan 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changesets/11755.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- fix(cli): Allow common custom scaffold template files (#11755) by @esteban-url

Adds the ability to generate test, stories and mock files when custom scaffold templates are in use.
2 changes: 1 addition & 1 deletion packages/cli/src/commands/destroy/scaffold/scaffold.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const tasks = ({ model, path, tests, nestScaffoldByModel }) =>

export const handler = async ({ model: modelArg }) => {
recordTelemetryAttributes({
command: 'destory scaffold',
command: 'destroy scaffold',
})
const { model, path } = splitPathAndModel(modelArg)
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,118 @@ export default ScaffoldLayout
"
`;

exports[`custom templates > creates a service 1`] = `
"import type {
QueryResolvers,
MutationResolvers,
PostRelationResolvers,
} from 'types/graphql'

import { db } from 'src/lib/db'

export const posts: QueryResolvers['posts'] = () => {
return db.post.findMany()
}

export const post: QueryResolvers['post'] = ({ id }) => {
return db.post.findUnique({
where: { id },
})
}

export const createPost: MutationResolvers['createPost'] = ({ input }) => {
return db.post.create({
data: input,
})
}

export const updatePost: MutationResolvers['updatePost'] = ({ id, input }) => {
return db.post.update({
data: input,
where: { id },
})
}

export const deletePost: MutationResolvers['deletePost'] = ({ id }) => {
return db.post.delete({
where: { id },
})
}

export const Post: PostRelationResolvers = {
favorites: (_obj, { root }) => {
return db.post.findUnique({ where: { id: root?.id } }).favorites()
},
tag: (_obj, { root }) => {
return db.post.findUnique({ where: { id: root?.id } }).tag()
},
}
"
`;

exports[`custom templates > creates a service test 1`] = `
"import type { Post } from '@prisma/client'

import { posts, post, createPost, updatePost, deletePost } from './posts'
import type { StandardScenario } from './posts.scenarios'

// Generated boilerplate tests do not account for all circumstances
// and can fail without adjustments, e.g. Float.
// Please refer to the RedwoodJS Testing Docs:
// https://redwoodjs.com/docs/testing#testing-services
// https://redwoodjs.com/docs/testing#jest-expect-type-considerations

describe('posts', () => {
scenario('returns all posts', async (scenario: StandardScenario) => {
const result = await posts()

expect(result.length).toEqual(Object.keys(scenario.post).length)
})

scenario('returns a single post', async (scenario: StandardScenario) => {
const result = await post({ id: scenario.post.one.id })

expect(result).toEqual(scenario.post.one)
})

scenario('creates a post', async () => {
const result = await createPost({
input: {
title: 'String',
slug: 'String1234567',
author: 'String',
body: 'String',
metadata: { foo: 'bar' },
},
})

expect(result.title).toEqual('String')
expect(result.slug).toEqual('String1234567')
expect(result.author).toEqual('String')
expect(result.body).toEqual('String')
expect(result.metadata).toEqual({ foo: 'bar' })
})

scenario('updates a post', async (scenario: StandardScenario) => {
const original = (await post({ id: scenario.post.one.id })) as Post
const result = await updatePost({
id: original.id,
input: { title: 'String2' },
})

expect(result.title).toEqual('String2')
})

scenario('deletes a post', async (scenario: StandardScenario) => {
const original = (await deletePost({ id: scenario.post.one.id })) as Post
const result = await post({ id: original.id })

expect(result).toEqual(null)
})
})
"
`;

exports[`custom templates > creates an sdl 1`] = `
"export const schema = gql\`
type Post {
Expand Down
155 changes: 149 additions & 6 deletions packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ describe('in javascript (default) mode', () => {
files = await scaffold.files({
...getDefaultArgs(defaults),
model: 'Post',
tests: true,
nestScaffoldByModel: true,
})
})
Expand Down Expand Up @@ -299,7 +298,6 @@ describe('in javascript (default) mode', () => {
scaffold.files({
...getDefaultArgs(defaults),
model: 'NoEditableField',
tests: true,
nestScaffoldByModel: true,
}),
).rejects.toThrow(
Expand Down Expand Up @@ -461,7 +459,6 @@ describe('in typescript mode', () => {
...getDefaultArgs(defaults),
model: 'Post',
typescript: true,
tests: true,
nestScaffoldByModel: true,
})
})
Expand Down Expand Up @@ -876,12 +873,84 @@ describe('custom templates', () => {
'redwood.toml': '',
'web/generators/scaffold/pages/EditNamePage.tsx.template':
'export default function CustomEditPage() { return null }',
'web/generators/scaffold/pages/EditNamePage.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/pages/EditNamePage.test.tsx.template':
"it('renders page successfully', () => {})",
'web/generators/scaffold/pages/NewNamePage.tsx.template':
'export default function CustomNewPage() { return null }',
'web/generators/scaffold/pages/NewNamePage.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/pages/NewNamePage.test.tsx.template':
"it('renders page successfully', () => {})",
'web/generators/scaffold/pages/NamePage.tsx.template':
'export default function CustomPage() { return null }',
'web/generators/scaffold/pages/NamePage.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/pages/NamePage.test.tsx.template':
"it('renders page successfully', () => {})",
'web/generators/scaffold/pages/NamesPage.tsx.template':
'export default function CustomPluralPage() { return null }',
'web/generators/scaffold/pages/NamesPage.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/pages/NamesPage.test.tsx.template':
"it('renders page successfully', () => {})",
'web/generators/scaffold/components/EditNameCell.tsx.template':
'export const Success = () => null',
'web/generators/scaffold/components/EditNameCell.mock.tsx.template':
'export const standard = () => ({})',
'web/generators/scaffold/components/EditNameCell.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/EditNameCell.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/components/NameCell.mock.tsx.template':
'export const standard = () => ({})',
'web/generators/scaffold/components/NameCell.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/NameCell.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/components/NameCell.tsx.template':
'export const Success = () => null',
'web/generators/scaffold/components/NameForm.tsx.template':
'export default function ${singularPascalName}Form() { return null }',
'web/generators/scaffold/components/NameForm.mock.tsx.template':
'export const standard = () => ({})',
'web/generators/scaffold/components/NameForm.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/NameForm.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/components/Names.tsx.template':
'export default function ${singularPascalName}List() { return null }',
'web/generators/scaffold/components/Names.mock.tsx.template':
'export const standard = () => ({})',
'web/generators/scaffold/components/Names.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/Names.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/components/NamesCell.tsx.template':
'export const Success = () => null',
'web/generators/scaffold/components/NamesCell.mock.tsx.template':
'export const standard = () => ({})',
'web/generators/scaffold/components/NamesCell.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/NamesCell.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/components/NewName.tsx.template':
'export default function New${singularPascalName}() { return null }',
'web/generators/scaffold/components/NewName.mock.tsx.template':
'export const standard = () => ({})',
'web/generators/scaffold/components/NewName.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/NewName.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
'web/generators/scaffold/components/Name.tsx.template':
'export default function ${singularPascalName}() { return null }',
'web/generators/scaffold/components/Name.mock.ts.template':
'export const standard = () => ({ custom: "" })',
'web/generators/scaffold/components/Name.test.tsx.template':
"it('renders component successfully', () => {})",
'web/generators/scaffold/components/Name.stories.tsx.template':
'const customMeta = {}\nexport default customMeta\nexport const Primary = {}',
},
process.env.RWJS_CWD,
)
Expand All @@ -890,8 +959,10 @@ describe('custom templates', () => {
force: false,
model: 'Post',
typescript: true,
tests: true,
nestScaffoldByModel: true,
tests: true,
stories: true,
serviceTests: true,
})
})

Expand All @@ -900,8 +971,8 @@ describe('custom templates', () => {
process.env.RWJS_CWD = originalRwjsCwd
})

test('returns exactly 19 files', () => {
expect(Object.keys(tsFiles).length).toEqual(19)
test('returns exactly 48 files', () => {
expect(Object.keys(tsFiles).length).toEqual(48)
})

test('creates an Edit page', async () => {
Expand Down Expand Up @@ -964,6 +1035,60 @@ describe('custom templates', () => {
`)
})

test('creates a Show page story', async () => {
expect(
tsFiles[
path.normalize(
'/path/to/project/web/src/pages/Post/PostPage/PostPage.stories.tsx',
)
],
).toMatchInlineSnapshot(`
"const customMeta = {}
export default customMeta
export const Primary = {}
"
`)
})

test('creates a Show page test', async () => {
expect(
tsFiles[
path.normalize(
'/path/to/project/web/src/pages/Post/PostPage/PostPage.test.tsx',
)
],
).toMatchInlineSnapshot(`
"it('renders page successfully', () => {})
"
`)
})

test('creates a test for the index component', async () => {
expect(
tsFiles[
path.normalize(
'/path/to/project/web/src/components/Post/Post/Post.test.tsx',
)
],
).toMatchInlineSnapshot(`
"it('renders component successfully', () => {})
"
`)
})

test('creates mocks for the index component', async () => {
expect(
tsFiles[
path.normalize(
'/path/to/project/web/src/components/Post/Post/Post.mock.ts',
)
],
).toMatchInlineSnapshot(`
"export const standard = () => ({ custom: '' })
"
`)
})

// SDL
// (Including this in the test just to make sure we're testing at least one
// api-side file)
Expand All @@ -974,6 +1099,24 @@ describe('custom templates', () => {
).toMatchSnapshot()
})

// Service

test('creates a service', () => {
expect(
tsFiles[
path.normalize('/path/to/project/api/src/services/posts/posts.ts')
],
).toMatchSnapshot()
})

test('creates a service test', () => {
expect(
tsFiles[
path.normalize('/path/to/project/api/src/services/posts/posts.test.ts')
],
).toMatchSnapshot()
})

// Layout
// (Including this in the test just to make sure we're testing at least one
// web-side file that we don't have a custom template for)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe('in javascript (default) mode', () => {
files = await scaffold.files({
...getDefaultArgs(defaults),
model: 'Post',
tests: true,
nestScaffoldByModel: false,
})
})
Expand Down Expand Up @@ -303,7 +302,6 @@ describe('in typescript mode', () => {
...getDefaultArgs(defaults),
model: 'Post',
typescript: true,
tests: true,
nestScaffoldByModel: false,
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe('admin/post', () => {
filesLower = await scaffold.files({
model: 'Post',
path: 'admin',
tests: true,
nestScaffoldByModel: true,
})
})
Expand Down Expand Up @@ -353,7 +352,6 @@ describe('Admin/Post', () => {
filesUpper = await scaffold.files({
model: 'Post',
path: 'Admin',
tests: true,
nestScaffoldByModel: true,
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe('admin/pages/post', () => {
filesNestedLower = await scaffold.files({
model: 'Post',
path: 'admin/pages',
tests: true,
nestScaffoldByModel: true,
})
})
Expand Down Expand Up @@ -363,7 +362,6 @@ describe('Admin/Pages/Post/Post', () => {
filesNestedUpper = await scaffold.files({
model: 'Post',
path: 'Admin/Pages',
tests: true,
nestScaffoldByModel: true,
})
})
Expand Down
Loading
Loading