From a51bd69842d618102aafc6f5bba4d8b935074a12 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 16 Aug 2022 12:41:47 +0200 Subject: [PATCH 01/22] Scaffolds: Don't generate unused methods --- .../__snapshots__/scaffold.test.js.snap | 79 ++++--------------- .../__snapshots__/scaffoldNoNest.test.js.snap | 79 ++++--------------- .../templates/components/Name.tsx.template | 10 +++ .../templates/components/Names.tsx.template | 13 ++- 4 files changed, 54 insertions(+), 127 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index 9dbdffb66627..201b7d120b6c 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -460,9 +460,7 @@ export const Success = ({ post }) => { `; exports[`in javascript (default) mode creates a show component 1`] = ` -"import humanize from 'humanize-string' - -import { Link, routes, navigate } from '@redwoodjs/router' +"import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' @@ -474,25 +472,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const formatEnum = (values) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values) - } - } -} - -const jsonDisplay = (obj) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} - const timeTag = (datetime) => { return ( datetime && ( @@ -1158,9 +1137,7 @@ export const Success = ({ posts }) => { `; exports[`in javascript (default) mode creates an index component 1`] = ` -"import humanize from 'humanize-string' - -import { Link, routes } from '@redwoodjs/router' +"import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' @@ -1176,17 +1153,6 @@ const DELETE_POST_MUTATION = gql\` const MAX_STRING_LENGTH = 150 -const formatEnum = (values) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values) - } - } -} - const truncate = (value) => { const output = value?.toString() if (output?.length > MAX_STRING_LENGTH) { @@ -1899,24 +1865,10 @@ const DELETE_POST_MUTATION = gql\` } \` -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} -const jsonDisplay = (obj: unknown) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} + + + const timeTag = (datetime?: string) => { return ( @@ -1928,10 +1880,13 @@ const timeTag = (datetime?: string) => { ) } + + const checkboxInputTag = (checked: boolean) => { return } + const Post = ({ post }: FindPostById) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { @@ -2665,16 +2620,8 @@ const DELETE_POST_MUTATION = gql\` const MAX_STRING_LENGTH = 150 -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} + + const truncate = (value: string | number) => { const output = value?.toString() @@ -2685,10 +2632,13 @@ const truncate = (value: string | number) => { } + const jsonTruncate = (obj: unknown) => { return truncate(JSON.stringify(obj, null, 2)) } + + const timeTag = (datetime?: string) => { return ( datetime && ( @@ -2699,10 +2649,13 @@ const timeTag = (datetime?: string) => { ) } + + const checkboxInputTag = (checked: boolean) => { return } + const PostsList = ({ posts }: FindPosts) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap index c7190a80c914..536e4b1ef0af 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap @@ -415,9 +415,7 @@ export const Success = ({ post }) => { `; exports[`in javascript (default) mode creates a show component 1`] = ` -"import humanize from 'humanize-string' - -import { Link, routes, navigate } from '@redwoodjs/router' +"import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' @@ -429,25 +427,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const formatEnum = (values) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values) - } - } -} - -const jsonDisplay = (obj) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} - const timeTag = (datetime) => { return ( datetime && ( @@ -1113,9 +1092,7 @@ export const Success = ({ posts }) => { `; exports[`in javascript (default) mode creates an index component 1`] = ` -"import humanize from 'humanize-string' - -import { Link, routes } from '@redwoodjs/router' +"import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' @@ -1131,17 +1108,6 @@ const DELETE_POST_MUTATION = gql\` const MAX_STRING_LENGTH = 150 -const formatEnum = (values) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values) - } - } -} - const truncate = (value) => { const output = value?.toString() if (output?.length > MAX_STRING_LENGTH) { @@ -1805,24 +1771,10 @@ const DELETE_POST_MUTATION = gql\` } \` -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} -const jsonDisplay = (obj: unknown) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} + + + const timeTag = (datetime?: string) => { return ( @@ -1834,10 +1786,13 @@ const timeTag = (datetime?: string) => { ) } + + const checkboxInputTag = (checked: boolean) => { return } + const Post = ({ post }: FindPostById) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { @@ -2571,16 +2526,8 @@ const DELETE_POST_MUTATION = gql\` const MAX_STRING_LENGTH = 150 -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} + + const truncate = (value: string | number) => { const output = value?.toString() @@ -2591,10 +2538,13 @@ const truncate = (value: string | number) => { } + const jsonTruncate = (obj: unknown) => { return truncate(JSON.stringify(obj, null, 2)) } + + const timeTag = (datetime?: string) => { return ( datetime && ( @@ -2605,10 +2555,13 @@ const timeTag = (datetime?: string) => { ) } + + const checkboxInputTag = (checked: boolean) => { return } + const PostsList = ({ posts }: FindPosts) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template index f54fe954031f..7b749bfd9efd 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template @@ -1,4 +1,6 @@ +<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> import humanize from 'humanize-string' +<% } %> import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' @@ -14,6 +16,7 @@ const DELETE_${singularConstantName}_MUTATION = gql` } ` +<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> const formatEnum = (values: string | string[] | null | undefined) => { if (values) { if (Array.isArray(values)) { @@ -24,7 +27,9 @@ const formatEnum = (values: string | string[] | null | undefined) => { } } } +<% } %> +<% if (columns.some((column) => column.listDisplayFunction === 'jsonDisplay')) { %> const jsonDisplay = (obj: unknown) => { return (
@@ -32,7 +37,9 @@ const jsonDisplay = (obj: unknown) => {
     
) } +<% } %> +<% if (columns.some((column) => column.listDisplayFunction === 'timeTag')) { %> const timeTag = (datetime?: string) => { return ( datetime && ( @@ -42,10 +49,13 @@ const timeTag = (datetime?: string) => { ) ) } +<% } %> +<% if (columns.some((column) => column.listDisplayFunction === 'checkboxInputTag')) { %> const checkboxInputTag = (checked: boolean) => { return } +<% } %> const ${singularPascalName} = ({ ${singularCamelName} }: Find${singularPascalName}ById) => { const [delete${singularPascalName}] = useMutation(DELETE_${singularConstantName}_MUTATION, { diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template index 87d6f8720b10..b5ffef96796e 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template @@ -1,4 +1,6 @@ +<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> import humanize from 'humanize-string' +<% } %> import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' @@ -18,6 +20,7 @@ const DELETE_${singularConstantName}_MUTATION = gql` const MAX_STRING_LENGTH = 150 +<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> const formatEnum = (values: string | string[] | null | undefined) => { if (values) { if (Array.isArray(values)) { @@ -28,7 +31,9 @@ const formatEnum = (values: string | string[] | null | undefined) => { } } } +<% } %> +<% if (columns.some((column) => column.listDisplayFunction === 'truncate')) { %> const truncate = (value: string | number) => { const output = value?.toString() if (output?.length > MAX_STRING_LENGTH) { @@ -36,12 +41,15 @@ const truncate = (value: string | number) => { } return output ?? '' } +<% } %> - +<% if (columns.some((column) => column.listDisplayFunction === 'jsonTruncate')) { %> const jsonTruncate = (obj: unknown) => { return truncate(JSON.stringify(obj, null, 2)) } +<% } %> +<% if (columns.some((column) => column.listDisplayFunction === 'timeTag')) { %> const timeTag = (datetime?: string) => { return ( datetime && ( @@ -51,10 +59,13 @@ const timeTag = (datetime?: string) => { ) ) } +<% } %> +<% if (columns.some((column) => column.listDisplayFunction === 'checkboxInputTag')) { %> const checkboxInputTag = (checked: boolean) => { return } +<% } %> const ${pluralPascalName}List = ({ ${pluralCamelName} }: Find${pluralPascalName}) => { const [delete${singularPascalName}] = useMutation(DELETE_${singularConstantName}_MUTATION, { From 8f047cdf85e477f027e910d174dd8cdbcb08a1ff Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 16 Aug 2022 13:48:51 +0200 Subject: [PATCH 02/22] Update test snapshots --- .../scaffold/__tests__/__snapshots__/scaffold.test.js.snap | 4 ++-- .../__tests__/__snapshots__/scaffoldNoNest.test.js.snap | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index 201b7d120b6c..89ad9551b92d 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -1849,7 +1849,7 @@ export const Success = ({ post }: CellSuccessProps) => { `; exports[`in typescript mode creates a show component 1`] = ` -"import humanize from 'humanize-string' +" import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' @@ -2600,7 +2600,7 @@ export const Success = ({ posts }: CellSuccessProps) => { `; exports[`in typescript mode creates an index component 1`] = ` -"import humanize from 'humanize-string' +" import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap index 536e4b1ef0af..e63ed341dc45 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap @@ -1755,7 +1755,7 @@ export const Success = ({ post }: CellSuccessProps) => { `; exports[`in typescript mode creates a show component 1`] = ` -"import humanize from 'humanize-string' +" import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' @@ -2506,7 +2506,7 @@ export const Success = ({ posts }: CellSuccessProps) => { `; exports[`in typescript mode creates an index component 1`] = ` -"import humanize from 'humanize-string' +" import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' From ec54e353f901ef2d39244101e0f57503724ceee8 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 16 Aug 2022 14:17:52 +0200 Subject: [PATCH 03/22] Updat test-project fixture --- .../migration.sql | 34 ------------------- .../migration.sql | 8 ----- .../api/src/services/posts/posts.scenarios.ts | 4 +-- .../api/src/services/users/users.scenarios.ts | 4 +-- __fixtures__/test-project/package.json | 5 +-- .../components/Contact/Contact/Contact.tsx | 24 ------------- .../components/Contact/Contacts/Contacts.tsx | 20 ----------- .../web/src/components/Post/Post/Post.tsx | 24 ------------- .../web/src/components/Post/Posts/Posts.tsx | 20 ----------- 9 files changed, 5 insertions(+), 138 deletions(-) delete mode 100644 __fixtures__/test-project/api/db/migrations/20220809160848_create_post_user/migration.sql delete mode 100644 __fixtures__/test-project/api/db/migrations/20220809160910_create_contact/migration.sql diff --git a/__fixtures__/test-project/api/db/migrations/20220809160848_create_post_user/migration.sql b/__fixtures__/test-project/api/db/migrations/20220809160848_create_post_user/migration.sql deleted file mode 100644 index 9dd73df9b6ac..000000000000 --- a/__fixtures__/test-project/api/db/migrations/20220809160848_create_post_user/migration.sql +++ /dev/null @@ -1,34 +0,0 @@ --- CreateTable -CREATE TABLE "UserExample" ( - "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - "email" TEXT NOT NULL, - "name" TEXT -); - --- CreateTable -CREATE TABLE "Post" ( - "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - "title" TEXT NOT NULL, - "body" TEXT NOT NULL, - "authorId" INTEGER NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "User" ( - "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - "email" TEXT NOT NULL, - "hashedPassword" TEXT NOT NULL, - "fullName" TEXT NOT NULL, - "salt" TEXT NOT NULL, - "resetToken" TEXT, - "resetTokenExpiresAt" DATETIME, - "roles" TEXT -); - --- CreateIndex -CREATE UNIQUE INDEX "UserExample_email_key" ON "UserExample"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/__fixtures__/test-project/api/db/migrations/20220809160910_create_contact/migration.sql b/__fixtures__/test-project/api/db/migrations/20220809160910_create_contact/migration.sql deleted file mode 100644 index 8d7bd91beb4d..000000000000 --- a/__fixtures__/test-project/api/db/migrations/20220809160910_create_contact/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- CreateTable -CREATE TABLE "Contact" ( - "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - "name" TEXT NOT NULL, - "email" TEXT NOT NULL, - "message" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); diff --git a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts index 981108da6d1c..f8f2af7f0a35 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String7710942', + email: 'String9705802', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String6732615', + email: 'String9366183', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts index 2ed62b388630..f888c8914ef3 100644 --- a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String1552241', + email: 'String5874613', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String7382597', + email: 'String7580776', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index f312b8c38faa..9e58b9f654ac 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -21,8 +21,5 @@ "prisma": { "seed": "yarn rw exec seed" }, - "packageManager": "yarn@3.2.2", - "resolutions": { - "jest": "28.1.3" - } + "packageManager": "yarn@3.2.2" } diff --git a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx index e97659f56c78..5b846e3b7f3d 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx @@ -1,4 +1,3 @@ -import humanize from 'humanize-string' import type { DeleteContactMutationVariables, FindContactById, @@ -16,25 +15,6 @@ const DELETE_CONTACT_MUTATION = gql` } ` -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} - -const jsonDisplay = (obj: unknown) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} - const timeTag = (datetime?: string) => { return ( datetime && ( @@ -45,10 +25,6 @@ const timeTag = (datetime?: string) => { ) } -const checkboxInputTag = (checked: boolean) => { - return -} - const Contact = ({ contact }: FindContactById) => { const [deleteContact] = useMutation(DELETE_CONTACT_MUTATION, { onCompleted: () => { diff --git a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx index 405d20056d6a..f8062e533da8 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx @@ -1,4 +1,3 @@ -import humanize from 'humanize-string' import type { DeleteContactMutationVariables, FindContacts, @@ -20,17 +19,6 @@ const DELETE_CONTACT_MUTATION = gql` const MAX_STRING_LENGTH = 150 -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} - const truncate = (value: string | number) => { const output = value?.toString() if (output?.length > MAX_STRING_LENGTH) { @@ -39,10 +27,6 @@ const truncate = (value: string | number) => { return output ?? '' } -const jsonTruncate = (obj: unknown) => { - return truncate(JSON.stringify(obj, null, 2)) -} - const timeTag = (datetime?: string) => { return ( datetime && ( @@ -53,10 +37,6 @@ const timeTag = (datetime?: string) => { ) } -const checkboxInputTag = (checked: boolean) => { - return -} - const ContactsList = ({ contacts }: FindContacts) => { const [deleteContact] = useMutation(DELETE_CONTACT_MUTATION, { onCompleted: () => { diff --git a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx index c3f44967d17d..b363fb20eed5 100644 --- a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx @@ -1,4 +1,3 @@ -import humanize from 'humanize-string' import type { DeletePostMutationVariables, FindPostById } from 'types/graphql' import { Link, routes, navigate } from '@redwoodjs/router' @@ -13,25 +12,6 @@ const DELETE_POST_MUTATION = gql` } ` -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} - -const jsonDisplay = (obj: unknown) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} - const timeTag = (datetime?: string) => { return ( datetime && ( @@ -42,10 +22,6 @@ const timeTag = (datetime?: string) => { ) } -const checkboxInputTag = (checked: boolean) => { - return -} - const Post = ({ post }: FindPostById) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { diff --git a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx index 818a85b4446d..aaa66a3e4a17 100644 --- a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx @@ -1,4 +1,3 @@ -import humanize from 'humanize-string' import type { DeletePostMutationVariables, FindPosts } from 'types/graphql' import { Link, routes } from '@redwoodjs/router' @@ -17,17 +16,6 @@ const DELETE_POST_MUTATION = gql` const MAX_STRING_LENGTH = 150 -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} - const truncate = (value: string | number) => { const output = value?.toString() if (output?.length > MAX_STRING_LENGTH) { @@ -36,10 +24,6 @@ const truncate = (value: string | number) => { return output ?? '' } -const jsonTruncate = (obj: unknown) => { - return truncate(JSON.stringify(obj, null, 2)) -} - const timeTag = (datetime?: string) => { return ( datetime && ( @@ -50,10 +34,6 @@ const timeTag = (datetime?: string) => { ) } -const checkboxInputTag = (checked: boolean) => { - return -} - const PostsList = ({ posts }: FindPosts) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { From 84c37ab79acb78d437caf28b27b3ed7c33a8c2dc Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 16 Aug 2022 15:22:00 +0200 Subject: [PATCH 04/22] Add missing test-project fixtures --- .../migration.sql | 34 +++++++++++++++++++ .../migration.sql | 8 +++++ 2 files changed, 42 insertions(+) create mode 100644 __fixtures__/test-project/api/db/migrations/20220816121217_create_post_user/migration.sql create mode 100644 __fixtures__/test-project/api/db/migrations/20220816121226_create_contact/migration.sql diff --git a/__fixtures__/test-project/api/db/migrations/20220816121217_create_post_user/migration.sql b/__fixtures__/test-project/api/db/migrations/20220816121217_create_post_user/migration.sql new file mode 100644 index 000000000000..9dd73df9b6ac --- /dev/null +++ b/__fixtures__/test-project/api/db/migrations/20220816121217_create_post_user/migration.sql @@ -0,0 +1,34 @@ +-- CreateTable +CREATE TABLE "UserExample" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "name" TEXT +); + +-- CreateTable +CREATE TABLE "Post" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "body" TEXT NOT NULL, + "authorId" INTEGER NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "User" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "hashedPassword" TEXT NOT NULL, + "fullName" TEXT NOT NULL, + "salt" TEXT NOT NULL, + "resetToken" TEXT, + "resetTokenExpiresAt" DATETIME, + "roles" TEXT +); + +-- CreateIndex +CREATE UNIQUE INDEX "UserExample_email_key" ON "UserExample"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/__fixtures__/test-project/api/db/migrations/20220816121226_create_contact/migration.sql b/__fixtures__/test-project/api/db/migrations/20220816121226_create_contact/migration.sql new file mode 100644 index 000000000000..8d7bd91beb4d --- /dev/null +++ b/__fixtures__/test-project/api/db/migrations/20220816121226_create_contact/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "Contact" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "message" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); From b03d69f425adb6217913dd58b2255f142553df81 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 16 Aug 2022 15:35:34 +0200 Subject: [PATCH 05/22] Re-add resolutions to test-project fixure --- __fixtures__/test-project/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index 9e58b9f654ac..f312b8c38faa 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -21,5 +21,8 @@ "prisma": { "seed": "yarn rw exec seed" }, - "packageManager": "yarn@3.2.2" + "packageManager": "yarn@3.2.2", + "resolutions": { + "jest": "28.1.3" + } } From b29f87d2c547ca2e1b2ed66faa11a04f6ed1d2a8 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 17 Aug 2022 16:12:01 +0200 Subject: [PATCH 06/22] Move formatting functions to a separate file --- packages/cli/src/commands/generate/helpers.js | 2 +- .../commands/generate/scaffold/scaffold.js | 41 ++++++++++++++ .../templates/components/Name.tsx.template | 47 +--------------- .../templates/components/Names.tsx.template | 54 +------------------ .../lib/formattingFunctions.tsx.template | 44 +++++++++++++++ 5 files changed, 89 insertions(+), 99 deletions(-) create mode 100644 packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template diff --git a/packages/cli/src/commands/generate/helpers.js b/packages/cli/src/commands/generate/helpers.js index 99d0e0d0a501..a3fe4bf4326c 100644 --- a/packages/cli/src/commands/generate/helpers.js +++ b/packages/cli/src/commands/generate/helpers.js @@ -17,7 +17,7 @@ import { pluralize, isPlural, isSingular } from '../../lib/rwPluralize' import { yargsDefaults } from '../generate' /** - * Returns the path to a custom generator template, if found in the app. + * Returns the full path to a custom generator template, if found in the app. * Otherwise the default Redwood template. */ export const customOrDefaultTemplatePath = ({ diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 280330dfc8cf..f6d8ae67b23d 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -181,6 +181,7 @@ export const files = async ({ typescript, })), ...assetFiles(name, tailwind), + ...formattingFunctions(name), ...layoutFiles(name, pascalScaffoldPath, typescript, templateStrings), ...(await pageFiles( name, @@ -236,6 +237,32 @@ const assetFiles = (name, tailwind) => { return fileList } +const formattingFunctions = (name) => { + const outputPath = path.join( + getPaths().web.src, + 'lib', + 'formattingFunctions.tsx.template' + ) + + // skip files that already exist on disk, never worry about overwriting + if (fs.existsSync(outputPath)) { + return + } + + const template = generateTemplate( + customOrDefaultTemplatePath({ + side: 'web', + generator: 'scaffold', + templatePath: path.join('lib', 'formattingFunctions.tsx.template'), + }), + { + name, + } + ) + + return { [outputPath]: template } +} + const layoutFiles = ( name, pascalScaffoldPath = '', @@ -487,6 +514,18 @@ const componentFiles = async ( }) ) + const formattingFunctionsImports = columns + .map((column) => column.displayFunction) + .sort() + .filter((v, i, a) => a.indexOf(v) === i) + .join(', ') + + const listFormattingFunctionsImports = columns + .map((column) => column.listDisplayFunction) + .sort() + .filter((v, i, a) => a.indexOf(v) === i) + .join(', ') + await asyncForEach(components, (component) => { const outputComponentName = component .replace(/Names/, pluralName) @@ -518,6 +557,8 @@ const componentFiles = async ( idType, intForeignKeys, pascalScaffoldPath, + listFormattingFunctionsImports, + formattingFunctionsImports, ...templateStrings, } ) diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template index 7b749bfd9efd..b5e241dd1665 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template @@ -1,11 +1,9 @@ -<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> -import humanize from 'humanize-string' -<% } %> - import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { ${formattingFunctionsImports} } from 'src/lib/formattingFunctions' + import type { Delete${singularPascalName}MutationVariables, Find${singularPascalName}ById } from 'types/graphql' const DELETE_${singularConstantName}_MUTATION = gql` @@ -16,47 +14,6 @@ const DELETE_${singularConstantName}_MUTATION = gql` } ` -<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'jsonDisplay')) { %> -const jsonDisplay = (obj: unknown) => { - return ( -
-      {JSON.stringify(obj, null, 2)}
-    
- ) -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'timeTag')) { %> -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'checkboxInputTag')) { %> -const checkboxInputTag = (checked: boolean) => { - return -} -<% } %> - const ${singularPascalName} = ({ ${singularCamelName} }: Find${singularPascalName}ById) => { const [delete${singularPascalName}] = useMutation(DELETE_${singularConstantName}_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template index b5ffef96796e..99eaeaac1858 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template @@ -1,12 +1,9 @@ -<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> -import humanize from 'humanize-string' -<% } %> - import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from '${importComponentNamesCell}' +import { ${listFormattingFunctionsImports} } from 'src/lib/formattingFunctions' import type { Delete${singularPascalName}MutationVariables, Find${pluralPascalName} } from 'types/graphql' @@ -18,55 +15,6 @@ const DELETE_${singularConstantName}_MUTATION = gql` } ` -const MAX_STRING_LENGTH = 150 - -<% if (columns.some((column) => column.listDisplayFunction === 'formatEnum')) { %> -const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } - } -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'truncate')) { %> -const truncate = (value: string | number) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'jsonTruncate')) { %> -const jsonTruncate = (obj: unknown) => { - return truncate(JSON.stringify(obj, null, 2)) -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'timeTag')) { %> -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} -<% } %> - -<% if (columns.some((column) => column.listDisplayFunction === 'checkboxInputTag')) { %> -const checkboxInputTag = (checked: boolean) => { - return -} -<% } %> - const ${pluralPascalName}List = ({ ${pluralCamelName} }: Find${pluralPascalName}) => { const [delete${singularPascalName}] = useMutation(DELETE_${singularConstantName}_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template new file mode 100644 index 000000000000..a6cb80500692 --- /dev/null +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template @@ -0,0 +1,44 @@ +import React from 'react' + +import humanize from 'humanize-string' + +const MAX_STRING_LENGTH = 150 + +export const formatEnum = (values: string | string[] | null | undefined) => { + if (values) { + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + return humanizedValues.join(', ') + } else { + return humanize(values as string) + } + } + + return '' +} + +export const truncate = (value: string | number) => { + const output = value?.toString() + if (output?.length > MAX_STRING_LENGTH) { + return output.substring(0, MAX_STRING_LENGTH) + '...' + } + return output ?? '' +} + +export const jsonTruncate = (obj: unknown) => { + return truncate(JSON.stringify(obj, null, 2)) +} + +export const timeTag = (datetime?: string) => { + return datetime ? ( + + ) : ( + '' + ) +} + +export const checkboxInputTag = (checked: boolean) => { + return +} From 6298ff835076277aaf2a573927abf621b961fd4f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 16 Sep 2022 16:44:54 +0200 Subject: [PATCH 07/22] Update fixture and start fixing tests --- .../api/src/services/posts/posts.scenarios.ts | 4 ++-- .../api/src/services/users/users.scenarios.ts | 4 ++-- __fixtures__/test-project/web/package.json | 2 +- .../components/Contact/Contact/Contact.tsx | 13 ++---------- .../components/Contact/Contacts/Contacts.tsx | 21 +------------------ .../web/src/components/Post/Post/Post.tsx | 13 ++---------- .../web/src/components/Post/Posts/Posts.tsx | 21 +------------------ .../scaffold/__tests__/scaffold.test.js | 8 +++---- .../scaffold/__tests__/scaffoldPath.test.js | 8 +++---- .../__tests__/scaffoldPathMulti.test.js | 8 +++---- .../__tests__/scaffoldPathMultiNoNest.test.js | 8 +++---- 11 files changed, 27 insertions(+), 83 deletions(-) diff --git a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts index 42cc9a13d002..ef592c02c29b 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String8713726', + email: 'String5415410', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String8610091', + email: 'String784374', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts index c27ef5149da3..fe691877156b 100644 --- a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String7466649', + email: 'String6361663', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String5806082', + email: 'String3297618', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/web/package.json b/__fixtures__/test-project/web/package.json index 67fa4a3522b5..f21062076f02 100644 --- a/__fixtures__/test-project/web/package.json +++ b/__fixtures__/test-project/web/package.json @@ -22,7 +22,7 @@ "react-dom": "17.0.2" }, "devDependencies": { - "autoprefixer": "^10.4.10", + "autoprefixer": "^10.4.11", "postcss": "^8.4.16", "postcss-loader": "^7.0.1", "prettier-plugin-tailwindcss": "^0.1.13", diff --git a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx index 657f902d5a8d..2d279099f93a 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx @@ -1,4 +1,3 @@ - import type { DeleteContactMutationVariables, FindContactById, @@ -8,6 +7,8 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { timeTag } from 'src/lib/formattingFunctions' + const DELETE_CONTACT_MUTATION = gql` mutation DeleteContactMutation($id: Int!) { deleteContact(id: $id) { @@ -16,16 +17,6 @@ const DELETE_CONTACT_MUTATION = gql` } ` -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - interface Props { contact: NonNullable } diff --git a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx index f8062e533da8..9b3b0a3db716 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx @@ -8,6 +8,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Contact/ContactsCell' +import { timeTag, truncate } from 'src/lib/formattingFunctions' const DELETE_CONTACT_MUTATION = gql` mutation DeleteContactMutation($id: Int!) { @@ -17,26 +18,6 @@ const DELETE_CONTACT_MUTATION = gql` } ` -const MAX_STRING_LENGTH = 150 - -const truncate = (value: string | number) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} - -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - const ContactsList = ({ contacts }: FindContacts) => { const [deleteContact] = useMutation(DELETE_CONTACT_MUTATION, { onCompleted: () => { diff --git a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx index cbfefd199fa3..94819e990faa 100644 --- a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx @@ -1,10 +1,11 @@ - import type { DeletePostMutationVariables, FindPostById } from 'types/graphql' import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { timeTag } from 'src/lib/formattingFunctions' + const DELETE_POST_MUTATION = gql` mutation DeletePostMutation($id: Int!) { deletePost(id: $id) { @@ -13,16 +14,6 @@ const DELETE_POST_MUTATION = gql` } ` -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - interface Props { post: NonNullable } diff --git a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx index aaa66a3e4a17..1663eee9abb1 100644 --- a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx @@ -5,6 +5,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' +import { timeTag, truncate } from 'src/lib/formattingFunctions' const DELETE_POST_MUTATION = gql` mutation DeletePostMutation($id: Int!) { @@ -14,26 +15,6 @@ const DELETE_POST_MUTATION = gql` } ` -const MAX_STRING_LENGTH = 150 - -const truncate = (value: string | number) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} - -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - const PostsList = ({ posts }: FindPosts) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js index 4c1cba67c735..3c269e8612a5 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js @@ -20,8 +20,8 @@ describe('in javascript (default) mode', () => { }) }) - test('returns exactly 17 files', async () => { - expect(Object.keys(files).length).toEqual(17) + test('returns exactly 18 files', async () => { + expect(Object.keys(files).length).toEqual(18) }) // SDL @@ -407,8 +407,8 @@ describe('in typescript mode', () => { }) }) - test('returns exactly 17 files', () => { - expect(Object.keys(tsFiles).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(tsFiles).length).toEqual(18) }) // SDL diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js index 1f04db4f52c8..6eba75307765 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesLower).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesLower).length).toEqual(18) }) // Layout @@ -344,8 +344,8 @@ describe('admin/post', () => { describe('Admin/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesUpper).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesUpper).length).toEqual(18) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js index 0aa93a001a5e..75b4de867e92 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/pages/post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesNestedLower).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesNestedLower).length).toEqual(18) }) // Layout @@ -354,8 +354,8 @@ describe('admin/pages/post', () => { describe('Admin/Pages/Post/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesNestedUpper).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesNestedUpper).length).toEqual(18) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js index 56764a1ee3ba..539d9543cc0c 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/pages/post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesNestedLower).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesNestedLower).length).toEqual(18) }) // Layout @@ -346,8 +346,8 @@ describe('admin/pages/post', () => { describe('Admin/Pages/Post/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesNestedUpper).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesNestedUpper).length).toEqual(18) }) // Layout From 876fbe4358774f1cae4d7f3da72f79c5353f80da Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 16 Sep 2022 18:12:24 +0200 Subject: [PATCH 08/22] Fix cli tests --- .../__snapshots__/scaffold.test.js.snap | 121 +++--------------- .../__snapshots__/scaffoldNoNest.test.js.snap | 121 +++--------------- .../scaffold/__tests__/scaffoldNoNest.test.js | 8 +- .../__tests__/scaffoldPathMultiword.test.js | 12 +- .../scaffoldPathMultiwordNoNest.test.js | 12 +- .../__tests__/scaffoldPathNoNest.test.js | 8 +- 6 files changed, 52 insertions(+), 230 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index 6d5064e032af..309f83a3116c 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -459,6 +459,12 @@ exports[`in javascript (default) mode creates a show component 1`] = ` import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { + checkboxInputTag, + jsonDisplay, + timeTag, +} from 'src/lib/formattingFunctions' + const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { deletePost(id: $id) { @@ -467,20 +473,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const timeTag = (datetime) => { - return ( - datetime && ( - - ) - ) -} - -const checkboxInputTag = (checked) => { - return -} - const Post = ({ post }) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { @@ -1129,6 +1121,12 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' +import { + checkboxInputTag, + jsonTruncate, + timeTag, + truncate, +} from 'src/lib/formattingFunctions' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1138,34 +1136,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const MAX_STRING_LENGTH = 150 - -const truncate = (value) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} - -const jsonTruncate = (obj) => { - return truncate(JSON.stringify(obj, null, 2)) -} - -const timeTag = (datetime) => { - return ( - datetime && ( - - ) - ) -} - -const checkboxInputTag = (checked) => { - return -} - const PostsList = ({ posts }) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { @@ -1810,11 +1780,12 @@ export const Success = ({ post }: CellSuccessProps) => { exports[`in typescript mode creates a show component 1`] = ` " - import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { checkboxInputTag, jsonDisplay, timeTag, } from 'src/lib/formattingFunctions' + import type { DeletePostMutationVariables, FindPostById } from 'types/graphql' const DELETE_POST_MUTATION = gql\` @@ -1825,27 +1796,6 @@ const DELETE_POST_MUTATION = gql\` } \` - - - - - -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - - - -const checkboxInputTag = (checked: boolean) => { - return -} - interface Props { post: NonNullable } @@ -2574,13 +2524,12 @@ export const Success = ({ posts }: CellSuccessProps) => { `; exports[`in typescript mode creates an index component 1`] = ` -" - -import { Link, routes } from '@redwoodjs/router' +"import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' +import { checkboxInputTag, jsonTruncate, timeTag, truncate } from 'src/lib/formattingFunctions' import type { DeletePostMutationVariables, FindPosts } from 'types/graphql' @@ -2592,44 +2541,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const MAX_STRING_LENGTH = 150 - - - - -const truncate = (value: string | number) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} - - - -const jsonTruncate = (obj: unknown) => { - return truncate(JSON.stringify(obj, null, 2)) -} - - - -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - - - -const checkboxInputTag = (checked: boolean) => { - return -} - - const PostsList = ({ posts }: FindPosts) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap index 257a14aabc7c..53aee9dfc6dc 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap @@ -415,6 +415,12 @@ exports[`in javascript (default) mode creates a show component 1`] = ` import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { + checkboxInputTag, + jsonDisplay, + timeTag, +} from 'src/lib/formattingFunctions' + const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { deletePost(id: $id) { @@ -423,20 +429,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const timeTag = (datetime) => { - return ( - datetime && ( - - ) - ) -} - -const checkboxInputTag = (checked) => { - return -} - const Post = ({ post }) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { @@ -1085,6 +1077,12 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/PostsCell' +import { + checkboxInputTag, + jsonTruncate, + timeTag, + truncate, +} from 'src/lib/formattingFunctions' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1094,34 +1092,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const MAX_STRING_LENGTH = 150 - -const truncate = (value) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} - -const jsonTruncate = (obj) => { - return truncate(JSON.stringify(obj, null, 2)) -} - -const timeTag = (datetime) => { - return ( - datetime && ( - - ) - ) -} - -const checkboxInputTag = (checked) => { - return -} - const PostsList = ({ posts }) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { @@ -1717,11 +1687,12 @@ export const Success = ({ post }: CellSuccessProps) => { exports[`in typescript mode creates a show component 1`] = ` " - import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' +import { checkboxInputTag, jsonDisplay, timeTag, } from 'src/lib/formattingFunctions' + import type { DeletePostMutationVariables, FindPostById } from 'types/graphql' const DELETE_POST_MUTATION = gql\` @@ -1732,27 +1703,6 @@ const DELETE_POST_MUTATION = gql\` } \` - - - - - -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - - - -const checkboxInputTag = (checked: boolean) => { - return -} - interface Props { post: NonNullable } @@ -2481,13 +2431,12 @@ export const Success = ({ posts }: CellSuccessProps) => { `; exports[`in typescript mode creates an index component 1`] = ` -" - -import { Link, routes } from '@redwoodjs/router' +"import { Link, routes } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/PostsCell' +import { checkboxInputTag, jsonTruncate, timeTag, truncate } from 'src/lib/formattingFunctions' import type { DeletePostMutationVariables, FindPosts } from 'types/graphql' @@ -2499,44 +2448,6 @@ const DELETE_POST_MUTATION = gql\` } \` -const MAX_STRING_LENGTH = 150 - - - - -const truncate = (value: string | number) => { - const output = value?.toString() - if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' - } - return output ?? '' -} - - - -const jsonTruncate = (obj: unknown) => { - return truncate(JSON.stringify(obj, null, 2)) -} - - - -const timeTag = (datetime?: string) => { - return ( - datetime && ( - - ) - ) -} - - - -const checkboxInputTag = (checked: boolean) => { - return -} - - const PostsList = ({ posts }: FindPosts) => { const [deletePost] = useMutation(DELETE_POST_MUTATION, { onCompleted: () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js index 298f883ac50b..a17caae2a700 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js @@ -20,8 +20,8 @@ describe('in javascript (default) mode', () => { }) }) - test('returns exactly 17 files', () => { - expect(Object.keys(files).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(files).length).toEqual(18) }) // SDL @@ -297,8 +297,8 @@ describe('in typescript mode', () => { }) }) - test('returns exactly 17 files', () => { - expect(Object.keys(tsFiles).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(tsFiles).length).toEqual(18) }) // SDL diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js index bf8f9b478ab3..460807f8a196 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js @@ -32,8 +32,8 @@ beforeAll(async () => { describe('AdminPages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesMultiwordUpper).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesMultiwordUpper).length).toEqual(18) }) // Layout @@ -362,8 +362,8 @@ describe('AdminPages/Post', () => { describe('admin-pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesMultiwordDash).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesMultiwordDash).length).toEqual(18) }) // Layout @@ -692,8 +692,8 @@ describe('admin-pages/Post', () => { describe('admin_pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesMultiwordUnderscore).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesMultiwordUnderscore).length).toEqual(18) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js index a6268bf0b08b..c8a7520b57c0 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js @@ -32,8 +32,8 @@ beforeAll(async () => { describe('AdminPages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesMultiwordUpper).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesMultiwordUpper).length).toEqual(18) }) // Layout @@ -354,8 +354,8 @@ describe('AdminPages/Post', () => { describe('admin-pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesMultiwordDash).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesMultiwordDash).length).toEqual(18) }) // Layout @@ -676,8 +676,8 @@ describe('admin-pages/Post', () => { describe('admin_pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesMultiwordUnderscore).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesMultiwordUnderscore).length).toEqual(18) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js index 76fb9f08fad7..9e39ff584edc 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesLower).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesLower).length).toEqual(18) }) // Layout @@ -342,8 +342,8 @@ describe('admin/Post', () => { describe('Admin/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 17 files', () => { - expect(Object.keys(filesUpper).length).toEqual(17) + test('returns exactly 18 files', () => { + expect(Object.keys(filesUpper).length).toEqual(18) }) // Layout From 1efe375099c07d5e14bb36e74c8963f96b27d3c0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 16 Sep 2022 22:53:44 +0200 Subject: [PATCH 09/22] Update formattingFunctions --- .../migration.sql | 34 +++++++++++++ .../migration.sql | 8 +++ .../api/src/services/posts/posts.scenarios.ts | 4 +- .../api/src/services/users/users.scenarios.ts | 4 +- .../src/lib/formattingFunctions.tsx.template | 50 +++++++++++++++++++ .../lib/formattingFunctions.tsx.template | 44 +++++++++------- 6 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 __fixtures__/test-project/api/db/migrations/20220916205220_create_post_user/migration.sql create mode 100644 __fixtures__/test-project/api/db/migrations/20220916205230_create_contact/migration.sql create mode 100644 __fixtures__/test-project/web/src/lib/formattingFunctions.tsx.template diff --git a/__fixtures__/test-project/api/db/migrations/20220916205220_create_post_user/migration.sql b/__fixtures__/test-project/api/db/migrations/20220916205220_create_post_user/migration.sql new file mode 100644 index 000000000000..9dd73df9b6ac --- /dev/null +++ b/__fixtures__/test-project/api/db/migrations/20220916205220_create_post_user/migration.sql @@ -0,0 +1,34 @@ +-- CreateTable +CREATE TABLE "UserExample" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "name" TEXT +); + +-- CreateTable +CREATE TABLE "Post" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "body" TEXT NOT NULL, + "authorId" INTEGER NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "User" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "hashedPassword" TEXT NOT NULL, + "fullName" TEXT NOT NULL, + "salt" TEXT NOT NULL, + "resetToken" TEXT, + "resetTokenExpiresAt" DATETIME, + "roles" TEXT +); + +-- CreateIndex +CREATE UNIQUE INDEX "UserExample_email_key" ON "UserExample"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/__fixtures__/test-project/api/db/migrations/20220916205230_create_contact/migration.sql b/__fixtures__/test-project/api/db/migrations/20220916205230_create_contact/migration.sql new file mode 100644 index 000000000000..8d7bd91beb4d --- /dev/null +++ b/__fixtures__/test-project/api/db/migrations/20220916205230_create_contact/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "Contact" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "message" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts index ef592c02c29b..15d4d3b0ef6f 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String5415410', + email: 'String3491400', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String784374', + email: 'String7126834', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts index fe691877156b..1bf38c792f2f 100644 --- a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String6361663', + email: 'String647232', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String3297618', + email: 'String7817769', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx.template b/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx.template new file mode 100644 index 000000000000..5761efb98df0 --- /dev/null +++ b/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx.template @@ -0,0 +1,50 @@ +import React from 'react' + +import humanize from 'humanize-string' + +const MAX_STRING_LENGTH = 150 + +export const formatEnum = (values: string | string[] | null | undefined) => { + let output = '' + + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + output = humanizedValues.join(', ') + } else if (values) { + output = humanize(values as string) + } + + return output +} + +export const truncate = (value: string | number) => { + let output = value?.toString() ?? '' + + if (output?.length > MAX_STRING_LENGTH) { + output = output.substring(0, MAX_STRING_LENGTH) + '...' + } + + return output +} + +export const jsonTruncate = (obj: unknown) => { + return truncate(JSON.stringify(obj, null, 2)) +} + +export const timeTag = (dateTime?: string) => { + let output: string | JSX.Element = '' + + if (dateTime) { + output = ( + + ) + } + + return output +} + +export const checkboxInputTag = (checked: boolean) => { + return +} diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template index a6cb80500692..5761efb98df0 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template @@ -5,38 +5,44 @@ import humanize from 'humanize-string' const MAX_STRING_LENGTH = 150 export const formatEnum = (values: string | string[] | null | undefined) => { - if (values) { - if (Array.isArray(values)) { - const humanizedValues = values.map((value) => humanize(value)) - return humanizedValues.join(', ') - } else { - return humanize(values as string) - } + let output = '' + + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + output = humanizedValues.join(', ') + } else if (values) { + output = humanize(values as string) } - return '' + return output } export const truncate = (value: string | number) => { - const output = value?.toString() + let output = value?.toString() ?? '' + if (output?.length > MAX_STRING_LENGTH) { - return output.substring(0, MAX_STRING_LENGTH) + '...' + output = output.substring(0, MAX_STRING_LENGTH) + '...' } - return output ?? '' + + return output } export const jsonTruncate = (obj: unknown) => { return truncate(JSON.stringify(obj, null, 2)) } -export const timeTag = (datetime?: string) => { - return datetime ? ( - - ) : ( - '' - ) +export const timeTag = (dateTime?: string) => { + let output: string | JSX.Element = '' + + if (dateTime) { + output = ( + + ) + } + + return output } export const checkboxInputTag = (checked: boolean) => { From d76cdf3e0d247b17fdf746a85e198b6792e5a5cd Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 17 Sep 2022 23:29:47 +0200 Subject: [PATCH 10/22] Fix formattingFunction output filename --- .../migration.sql | 0 .../migration.sql | 0 .../test-project/api/src/services/posts/posts.scenarios.ts | 4 ++-- .../test-project/api/src/services/users/users.scenarios.ts | 4 ++-- packages/cli/src/commands/generate/scaffold/scaffold.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename __fixtures__/test-project/api/db/migrations/{20220916205220_create_post_user => 20220917105453_create_post_user}/migration.sql (100%) rename __fixtures__/test-project/api/db/migrations/{20220916205230_create_contact => 20220917105506_create_contact}/migration.sql (100%) diff --git a/__fixtures__/test-project/api/db/migrations/20220916205220_create_post_user/migration.sql b/__fixtures__/test-project/api/db/migrations/20220917105453_create_post_user/migration.sql similarity index 100% rename from __fixtures__/test-project/api/db/migrations/20220916205220_create_post_user/migration.sql rename to __fixtures__/test-project/api/db/migrations/20220917105453_create_post_user/migration.sql diff --git a/__fixtures__/test-project/api/db/migrations/20220916205230_create_contact/migration.sql b/__fixtures__/test-project/api/db/migrations/20220917105506_create_contact/migration.sql similarity index 100% rename from __fixtures__/test-project/api/db/migrations/20220916205230_create_contact/migration.sql rename to __fixtures__/test-project/api/db/migrations/20220917105506_create_contact/migration.sql diff --git a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts index 15d4d3b0ef6f..fcd1894b2875 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String3491400', + email: 'String8893764', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String7126834', + email: 'String3198698', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts index 1bf38c792f2f..a0449bc99a23 100644 --- a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String647232', + email: 'String1893596', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String7817769', + email: 'String6766410', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index f6d8ae67b23d..8d5d602516b6 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -241,7 +241,7 @@ const formattingFunctions = (name) => { const outputPath = path.join( getPaths().web.src, 'lib', - 'formattingFunctions.tsx.template' + 'formattingFunctions.tsx' ) // skip files that already exist on disk, never worry about overwriting From 78f491b84f8d6df773f0b63144fc50ce8976ec81 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 17 Sep 2022 23:49:58 +0200 Subject: [PATCH 11/22] Update test project fixture --- .../migration.sql | 0 .../migration.sql | 0 .../test-project/api/src/services/posts/posts.scenarios.ts | 4 ++-- .../test-project/api/src/services/users/users.scenarios.ts | 4 ++-- ...rmattingFunctions.tsx.template => formattingFunctions.tsx} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename __fixtures__/test-project/api/db/migrations/{20220917105453_create_post_user => 20220917213258_create_post_user}/migration.sql (100%) rename __fixtures__/test-project/api/db/migrations/{20220917105506_create_contact => 20220917213313_create_contact}/migration.sql (100%) rename __fixtures__/test-project/web/src/lib/{formattingFunctions.tsx.template => formattingFunctions.tsx} (100%) diff --git a/__fixtures__/test-project/api/db/migrations/20220917105453_create_post_user/migration.sql b/__fixtures__/test-project/api/db/migrations/20220917213258_create_post_user/migration.sql similarity index 100% rename from __fixtures__/test-project/api/db/migrations/20220917105453_create_post_user/migration.sql rename to __fixtures__/test-project/api/db/migrations/20220917213258_create_post_user/migration.sql diff --git a/__fixtures__/test-project/api/db/migrations/20220917105506_create_contact/migration.sql b/__fixtures__/test-project/api/db/migrations/20220917213313_create_contact/migration.sql similarity index 100% rename from __fixtures__/test-project/api/db/migrations/20220917105506_create_contact/migration.sql rename to __fixtures__/test-project/api/db/migrations/20220917213313_create_contact/migration.sql diff --git a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts index fcd1894b2875..1c25870e053b 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String8893764', + email: 'String9519070', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String3198698', + email: 'String6435942', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts index a0449bc99a23..8ad38019b53e 100644 --- a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String1893596', + email: 'String854868', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String6766410', + email: 'String1526569', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx.template b/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx similarity index 100% rename from __fixtures__/test-project/web/src/lib/formattingFunctions.tsx.template rename to __fixtures__/test-project/web/src/lib/formattingFunctions.tsx From 6db35378dfb757d42f7aa068d860742f600b4526 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 18 Sep 2022 01:35:51 +0200 Subject: [PATCH 12/22] Add tests for formattingFunctions --- .../commands/generate/scaffold/scaffold.js | 21 ++- .../lib/formattingFunctions.test.tsx.template | 127 ++++++++++++++++++ .../lib/formattingFunctions.tsx.template | 6 +- 3 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 8d5d602516b6..4f14cd770869 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -243,6 +243,11 @@ const formattingFunctions = (name) => { 'lib', 'formattingFunctions.tsx' ) + const outputPathTest = path.join( + getPaths().web.src, + 'lib', + 'formattingFunctions.test.tsx' + ) // skip files that already exist on disk, never worry about overwriting if (fs.existsSync(outputPath)) { @@ -260,7 +265,21 @@ const formattingFunctions = (name) => { } ) - return { [outputPath]: template } + const templateTest = generateTemplate( + customOrDefaultTemplatePath({ + side: 'web', + generator: 'scaffold', + templatePath: path.join('lib', 'formattingFunctions.test.tsx.template'), + }), + { + name, + } + ) + + return { + [outputPath]: template, + [outputPathTest]: templateTest, + } } const layoutFiles = ( diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template new file mode 100644 index 000000000000..a9830db8746b --- /dev/null +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template @@ -0,0 +1,127 @@ +import { render, waitFor } from '@redwoodjs/testing/web' + +import { + formatEnum, + jsonTruncate, + truncate, + timeTag, + checkboxInputTag, +} from './formattingFunctions' + +describe('formatEnum', () => { + it('handles nullish values', () => { + expect(formatEnum(null)).toEqual('') + expect(formatEnum('')).toEqual('') + expect(formatEnum(undefined)).toEqual('') + }) + + it('formats a list of values', () => { + expect( + formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET']) + ).toEqual('Red, Orange, Yellow, Green, Blue, Violet') + }) + + it('formats a single value', () => { + expect(formatEnum('DARK_BLUE')).toEqual('Dark blue') + }) + + it('returns an empty string for values of the wrong type (for JS projects)', () => { + // @ts-expect-error - Testing JS scenario + expect(formatEnum(5)).toEqual('') + }) +}) + +describe('truncate', () => { + it('truncates really long strings', () => { + expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000) + expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/) + }) + + it('does not modify short strings', () => { + expect(truncate('Short strinG')).toEqual('Short strinG') + }) + + it('adds ... to the end of truncated strings', () => { + expect(truncate('repeat'.repeat(1000))).toMatch(/\w\.\.\.$/) + }) + + it('accepts numbers', () => { + expect(truncate(123)).toEqual('123') + expect(truncate(0)).toEqual('0') + expect(truncate(0o000)).toEqual('0') + }) + + it('handles arguments of invalid type', () => { + // @ts-expect-error - Testing JS scenario + expect(truncate(false)).toEqual('false') + + expect(truncate(undefined)).toEqual('') + expect(truncate(null)).toEqual('') + }) +}) + +describe('jsonTruncate', () => { + it('truncates large json structures', () => { + expect( + jsonTruncate({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + kittens: 'kittens meow', + bazinga: 'Sheldon', + nested: { + foobar: 'I have no imagination', + two: 'Second nested item', + }, + five: 5, + bool: false, + }) + ).toMatch(/.+\n.+\w\.\.\.$/s) + }) +}) + +describe('timeTag', () => { + it('renders a date', async () => { + const time = timeTag(new Date('1970-08-20').toUTCString()) + + const screen = render(
{time}
) + + await waitFor(() => screen.getByText(/1970.*00:00:00/)) + }) + + it('can take an empty input string', async () => { + const time = timeTag('') + + expect(time).toEqual('') + }) +}) + +describe('checkboxInputTag', () => { + it('can be checked', () => { + const checkbox = checkboxInputTag(true) + + const screen = render(checkbox) + expect(screen.getByRole('checkbox')).toBeChecked() + }) + + it('can be unchecked', () => { + const checkbox = checkboxInputTag(false) + + const screen = render(checkbox) + expect(screen.getByRole('checkbox')).not.toBeChecked() + }) + + it('is disabled when checked', () => { + const checkbox = checkboxInputTag(true) + + const screen = render(checkbox) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) + + it('is disabled when unchecked', () => { + const checkbox = checkboxInputTag(false) + + const screen = render(checkbox) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) +}) diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template index 5761efb98df0..30552293578e 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template @@ -10,8 +10,8 @@ export const formatEnum = (values: string | string[] | null | undefined) => { if (Array.isArray(values)) { const humanizedValues = values.map((value) => humanize(value)) output = humanizedValues.join(', ') - } else if (values) { - output = humanize(values as string) + } else if (typeof values === 'string') { + output = humanize(values) } return output @@ -20,7 +20,7 @@ export const formatEnum = (values: string | string[] | null | undefined) => { export const truncate = (value: string | number) => { let output = value?.toString() ?? '' - if (output?.length > MAX_STRING_LENGTH) { + if (output.length > MAX_STRING_LENGTH) { output = output.substring(0, MAX_STRING_LENGTH) + '...' } From 4429a0eb313ed1983dd4a6f9301ce4ab784be874 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 18 Sep 2022 01:52:47 +0200 Subject: [PATCH 13/22] scaffold 19 files --- .../generate/scaffold/__tests__/scaffold.test.js | 8 ++++---- .../scaffold/__tests__/scaffoldNoNest.test.js | 8 ++++---- .../generate/scaffold/__tests__/scaffoldPath.test.js | 8 ++++---- .../scaffold/__tests__/scaffoldPathMulti.test.js | 8 ++++---- .../__tests__/scaffoldPathMultiNoNest.test.js | 8 ++++---- .../scaffold/__tests__/scaffoldPathMultiword.test.js | 12 ++++++------ .../__tests__/scaffoldPathMultiwordNoNest.test.js | 12 ++++++------ .../scaffold/__tests__/scaffoldPathNoNest.test.js | 8 ++++---- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js index 3c269e8612a5..da2f369d65fd 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js @@ -20,8 +20,8 @@ describe('in javascript (default) mode', () => { }) }) - test('returns exactly 18 files', async () => { - expect(Object.keys(files).length).toEqual(18) + test('returns exactly 19 files', async () => { + expect(Object.keys(files).length).toEqual(19) }) // SDL @@ -407,8 +407,8 @@ describe('in typescript mode', () => { }) }) - test('returns exactly 18 files', () => { - expect(Object.keys(tsFiles).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(tsFiles).length).toEqual(19) }) // SDL diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js index a17caae2a700..39f72be1741f 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js @@ -20,8 +20,8 @@ describe('in javascript (default) mode', () => { }) }) - test('returns exactly 18 files', () => { - expect(Object.keys(files).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(files).length).toEqual(19) }) // SDL @@ -297,8 +297,8 @@ describe('in typescript mode', () => { }) }) - test('returns exactly 18 files', () => { - expect(Object.keys(tsFiles).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(tsFiles).length).toEqual(19) }) // SDL diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js index 6eba75307765..70cb76fc7363 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesLower).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesLower).length).toEqual(19) }) // Layout @@ -344,8 +344,8 @@ describe('admin/post', () => { describe('Admin/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesUpper).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesUpper).length).toEqual(19) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js index 75b4de867e92..b8d14a806859 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/pages/post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesNestedLower).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesNestedLower).length).toEqual(19) }) // Layout @@ -354,8 +354,8 @@ describe('admin/pages/post', () => { describe('Admin/Pages/Post/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesNestedUpper).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesNestedUpper).length).toEqual(19) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js index 539d9543cc0c..a5e77f991c9d 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/pages/post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesNestedLower).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesNestedLower).length).toEqual(19) }) // Layout @@ -346,8 +346,8 @@ describe('admin/pages/post', () => { describe('Admin/Pages/Post/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesNestedUpper).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesNestedUpper).length).toEqual(19) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js index 460807f8a196..420fbf36ffc3 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js @@ -32,8 +32,8 @@ beforeAll(async () => { describe('AdminPages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesMultiwordUpper).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesMultiwordUpper).length).toEqual(19) }) // Layout @@ -362,8 +362,8 @@ describe('AdminPages/Post', () => { describe('admin-pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesMultiwordDash).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesMultiwordDash).length).toEqual(19) }) // Layout @@ -692,8 +692,8 @@ describe('admin-pages/Post', () => { describe('admin_pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesMultiwordUnderscore).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesMultiwordUnderscore).length).toEqual(19) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js index c8a7520b57c0..eeb81e76ccc4 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js @@ -32,8 +32,8 @@ beforeAll(async () => { describe('AdminPages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesMultiwordUpper).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesMultiwordUpper).length).toEqual(19) }) // Layout @@ -354,8 +354,8 @@ describe('AdminPages/Post', () => { describe('admin-pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesMultiwordDash).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesMultiwordDash).length).toEqual(19) }) // Layout @@ -676,8 +676,8 @@ describe('admin-pages/Post', () => { describe('admin_pages/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesMultiwordUnderscore).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesMultiwordUnderscore).length).toEqual(19) }) // Layout diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js index 9e39ff584edc..0ac66f8da818 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js @@ -24,8 +24,8 @@ beforeAll(async () => { describe('admin/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesLower).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesLower).length).toEqual(19) }) // Layout @@ -342,8 +342,8 @@ describe('admin/Post', () => { describe('Admin/Post', () => { describe('creates the correct files with the correct imports', () => { - test('returns exactly 18 files', () => { - expect(Object.keys(filesUpper).length).toEqual(18) + test('returns exactly 19 files', () => { + expect(Object.keys(filesUpper).length).toEqual(19) }) // Layout From 5908b10f2ee782d55d1b8dd7c3f4fcdfdc97759d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 18 Sep 2022 09:58:19 +0200 Subject: [PATCH 14/22] Import screen --- .../lib/formattingFunctions.test.tsx.template | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template index a9830db8746b..4c40a95fd725 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template @@ -1,4 +1,4 @@ -import { render, waitFor } from '@redwoodjs/testing/web' +import { render, waitFor, screen } from '@redwoodjs/testing/web' import { formatEnum, @@ -82,46 +82,34 @@ describe('jsonTruncate', () => { describe('timeTag', () => { it('renders a date', async () => { - const time = timeTag(new Date('1970-08-20').toUTCString()) - - const screen = render(
{time}
) + render(
{timeTag(new Date('1970-08-20').toUTCString())}
) await waitFor(() => screen.getByText(/1970.*00:00:00/)) }) it('can take an empty input string', async () => { - const time = timeTag('') - - expect(time).toEqual('') + expect(timeTag('')).toEqual('') }) }) describe('checkboxInputTag', () => { it('can be checked', () => { - const checkbox = checkboxInputTag(true) - - const screen = render(checkbox) + render(checkboxInputTag(true)) expect(screen.getByRole('checkbox')).toBeChecked() }) it('can be unchecked', () => { - const checkbox = checkboxInputTag(false) - - const screen = render(checkbox) + render(checkboxInputTag(false)) expect(screen.getByRole('checkbox')).not.toBeChecked() }) it('is disabled when checked', () => { - const checkbox = checkboxInputTag(true) - - const screen = render(checkbox) + render(checkboxInputTag(true)) expect(screen.getByRole('checkbox')).toBeDisabled() }) it('is disabled when unchecked', () => { - const checkbox = checkboxInputTag(false) - - const screen = render(checkbox) + render(checkboxInputTag(false)) expect(screen.getByRole('checkbox')).toBeDisabled() }) }) From 7fbb0c53ec9445cf44dfd1820074568ffcfe7540 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 18 Sep 2022 11:46:50 +0200 Subject: [PATCH 15/22] Change name of formatters file --- .../migration.sql | 0 .../migration.sql | 0 .../api/src/services/posts/posts.scenarios.ts | 4 +- .../api/src/services/users/users.scenarios.ts | 4 +- .../components/Contact/Contact/Contact.tsx | 2 +- .../components/Contact/Contacts/Contacts.tsx | 2 +- .../web/src/components/Post/Post/Post.tsx | 2 +- .../web/src/components/Post/Posts/Posts.tsx | 2 +- .../web/src/lib/formatters.test.tsx | 2 +- .../test-project/web/src/lib/formatters.tsx | 0 .../__snapshots__/scaffold.test.js.snap | 132 +++++++++--------- .../__snapshots__/scaffoldNoNest.test.js.snap | 132 +++++++++--------- .../commands/generate/scaffold/scaffold.js | 24 ++-- .../templates/components/Name.tsx.template | 2 +- .../templates/components/Names.tsx.template | 2 +- .../lib/formatters.test.tsx.template | 115 +++++++++++++++ .../templates/lib/formatters.tsx.template | 6 +- 17 files changed, 271 insertions(+), 160 deletions(-) rename __fixtures__/test-project/api/db/migrations/{20220917213258_create_post_user => 20220918091756_create_post_user}/migration.sql (100%) rename __fixtures__/test-project/api/db/migrations/{20220917213313_create_contact => 20220918091807_create_contact}/migration.sql (100%) rename packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template => __fixtures__/test-project/web/src/lib/formatters.test.tsx (98%) rename packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template => __fixtures__/test-project/web/src/lib/formatters.tsx (100%) create mode 100644 packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template rename __fixtures__/test-project/web/src/lib/formattingFunctions.tsx => packages/cli/src/commands/generate/scaffold/templates/lib/formatters.tsx.template (89%) diff --git a/__fixtures__/test-project/api/db/migrations/20220917213258_create_post_user/migration.sql b/__fixtures__/test-project/api/db/migrations/20220918091756_create_post_user/migration.sql similarity index 100% rename from __fixtures__/test-project/api/db/migrations/20220917213258_create_post_user/migration.sql rename to __fixtures__/test-project/api/db/migrations/20220918091756_create_post_user/migration.sql diff --git a/__fixtures__/test-project/api/db/migrations/20220917213313_create_contact/migration.sql b/__fixtures__/test-project/api/db/migrations/20220918091807_create_contact/migration.sql similarity index 100% rename from __fixtures__/test-project/api/db/migrations/20220917213313_create_contact/migration.sql rename to __fixtures__/test-project/api/db/migrations/20220918091807_create_contact/migration.sql diff --git a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts index 1c25870e053b..db7e35d2dd4a 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String9519070', + email: 'String4857147', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String6435942', + email: 'String1125871', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts index 8ad38019b53e..26207f6aaaad 100644 --- a/__fixtures__/test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String854868', + email: 'String4815975', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String1526569', + email: 'String3376651', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx index 2d279099f93a..9c158a46424d 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx @@ -7,7 +7,7 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { timeTag } from 'src/lib/formattingFunctions' +import { timeTag } from 'src/lib/formatters' const DELETE_CONTACT_MUTATION = gql` mutation DeleteContactMutation($id: Int!) { diff --git a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx index 9b3b0a3db716..15a026762614 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx @@ -8,7 +8,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Contact/ContactsCell' -import { timeTag, truncate } from 'src/lib/formattingFunctions' +import { timeTag, truncate } from 'src/lib/formatters' const DELETE_CONTACT_MUTATION = gql` mutation DeleteContactMutation($id: Int!) { diff --git a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx index 94819e990faa..2fc068d337fa 100644 --- a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx @@ -4,7 +4,7 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { timeTag } from 'src/lib/formattingFunctions' +import { timeTag } from 'src/lib/formatters' const DELETE_POST_MUTATION = gql` mutation DeletePostMutation($id: Int!) { diff --git a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx index 1663eee9abb1..168e24165a36 100644 --- a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx @@ -5,7 +5,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' -import { timeTag, truncate } from 'src/lib/formattingFunctions' +import { timeTag, truncate } from 'src/lib/formatters' const DELETE_POST_MUTATION = gql` mutation DeletePostMutation($id: Int!) { diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template b/__fixtures__/test-project/web/src/lib/formatters.test.tsx similarity index 98% rename from packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template rename to __fixtures__/test-project/web/src/lib/formatters.test.tsx index 4c40a95fd725..d4e8dc5a19c8 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.test.tsx.template +++ b/__fixtures__/test-project/web/src/lib/formatters.test.tsx @@ -6,7 +6,7 @@ import { truncate, timeTag, checkboxInputTag, -} from './formattingFunctions' +} from './formatters' describe('formatEnum', () => { it('handles nullish values', () => { diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template b/__fixtures__/test-project/web/src/lib/formatters.tsx similarity index 100% rename from packages/cli/src/commands/generate/scaffold/templates/lib/formattingFunctions.tsx.template rename to __fixtures__/test-project/web/src/lib/formatters.tsx diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index 309f83a3116c..a2c55cc4b0dd 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -463,7 +463,7 @@ import { checkboxInputTag, jsonDisplay, timeTag, -} from 'src/lib/formattingFunctions' +} from 'src/lib/formatters' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1126,7 +1126,7 @@ import { jsonTruncate, timeTag, truncate, -} from 'src/lib/formattingFunctions' +} from 'src/lib/formatters' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1284,43 +1284,43 @@ interface PostFormProps { const PostForm = (props: PostFormProps) => { const onSubmit = (data: FormPost) => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + props.onSave(data, props?.post?.id) } @@ -1333,7 +1333,7 @@ const PostForm = (props: PostFormProps) => { titleClassName="rw-form-error-title" listClassName="rw-form-error-list" /> - + - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1360,7 +1360,7 @@ const PostForm = (props: PostFormProps) => { > Slug - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1379,7 +1379,7 @@ const PostForm = (props: PostFormProps) => { > Author - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1398,7 +1398,7 @@ const PostForm = (props: PostFormProps) => { > Body - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1417,14 +1417,14 @@ const PostForm = (props: PostFormProps) => { > Image - + - + @@ -1435,14 +1435,14 @@ const PostForm = (props: PostFormProps) => { > Posted at - + - + @@ -1453,14 +1453,14 @@ const PostForm = (props: PostFormProps) => { > Is pinned - + - + @@ -1471,7 +1471,7 @@ const PostForm = (props: PostFormProps) => { > Read time - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true, required: true }} /> - + @@ -1490,7 +1490,7 @@ const PostForm = (props: PostFormProps) => { > Rating - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true }} /> - + @@ -1509,7 +1509,7 @@ const PostForm = (props: PostFormProps) => { > Upvotes - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1528,7 +1528,7 @@ const PostForm = (props: PostFormProps) => { > Metadata - + { errorClassName="rw-input rw-input-error" validation={{ valueAsJSON: true, required: true }} /> - + @@ -1547,7 +1547,7 @@ const PostForm = (props: PostFormProps) => { > Huge number - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1784,7 +1784,7 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { checkboxInputTag, jsonDisplay, timeTag, } from 'src/lib/formattingFunctions' +import { checkboxInputTag, jsonDisplay, timeTag, } from 'src/lib/formatters' import type { DeletePostMutationVariables, FindPostById } from 'types/graphql' @@ -2529,7 +2529,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' -import { checkboxInputTag, jsonTruncate, timeTag, truncate } from 'src/lib/formattingFunctions' +import { checkboxInputTag, jsonTruncate, timeTag, truncate } from 'src/lib/formatters' import type { DeletePostMutationVariables, FindPosts } from 'types/graphql' diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap index 53aee9dfc6dc..148e87f4b75b 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap @@ -419,7 +419,7 @@ import { checkboxInputTag, jsonDisplay, timeTag, -} from 'src/lib/formattingFunctions' +} from 'src/lib/formatters' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1082,7 +1082,7 @@ import { jsonTruncate, timeTag, truncate, -} from 'src/lib/formattingFunctions' +} from 'src/lib/formatters' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1240,43 +1240,43 @@ interface PostFormProps { const PostForm = (props: PostFormProps) => { const onSubmit = (data: FormPost) => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + props.onSave(data, props?.post?.id) } @@ -1289,7 +1289,7 @@ const PostForm = (props: PostFormProps) => { titleClassName="rw-form-error-title" listClassName="rw-form-error-list" /> - + - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1316,7 +1316,7 @@ const PostForm = (props: PostFormProps) => { > Slug - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1335,7 +1335,7 @@ const PostForm = (props: PostFormProps) => { > Author - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1354,7 +1354,7 @@ const PostForm = (props: PostFormProps) => { > Body - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1373,14 +1373,14 @@ const PostForm = (props: PostFormProps) => { > Image - + - + @@ -1391,14 +1391,14 @@ const PostForm = (props: PostFormProps) => { > Posted at - + - + @@ -1409,14 +1409,14 @@ const PostForm = (props: PostFormProps) => { > Is pinned - + - + @@ -1427,7 +1427,7 @@ const PostForm = (props: PostFormProps) => { > Read time - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true, required: true }} /> - + @@ -1446,7 +1446,7 @@ const PostForm = (props: PostFormProps) => { > Rating - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true }} /> - + @@ -1465,7 +1465,7 @@ const PostForm = (props: PostFormProps) => { > Upvotes - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1484,7 +1484,7 @@ const PostForm = (props: PostFormProps) => { > Metadata - + { errorClassName="rw-input rw-input-error" validation={{ valueAsJSON: true, required: true }} /> - + @@ -1503,7 +1503,7 @@ const PostForm = (props: PostFormProps) => { > Huge number - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1691,7 +1691,7 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { checkboxInputTag, jsonDisplay, timeTag, } from 'src/lib/formattingFunctions' +import { checkboxInputTag, jsonDisplay, timeTag, } from 'src/lib/formatters' import type { DeletePostMutationVariables, FindPostById } from 'types/graphql' @@ -2436,7 +2436,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/PostsCell' -import { checkboxInputTag, jsonTruncate, timeTag, truncate } from 'src/lib/formattingFunctions' +import { checkboxInputTag, jsonTruncate, timeTag, truncate } from 'src/lib/formatters' import type { DeletePostMutationVariables, FindPosts } from 'types/graphql' diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 4f14cd770869..876c04a753fd 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -181,7 +181,7 @@ export const files = async ({ typescript, })), ...assetFiles(name, tailwind), - ...formattingFunctions(name), + ...formatters(name), ...layoutFiles(name, pascalScaffoldPath, typescript, templateStrings), ...(await pageFiles( name, @@ -237,16 +237,12 @@ const assetFiles = (name, tailwind) => { return fileList } -const formattingFunctions = (name) => { - const outputPath = path.join( - getPaths().web.src, - 'lib', - 'formattingFunctions.tsx' - ) +const formatters = (name) => { + const outputPath = path.join(getPaths().web.src, 'lib', 'formatters.tsx') const outputPathTest = path.join( getPaths().web.src, 'lib', - 'formattingFunctions.test.tsx' + 'formatters.test.tsx' ) // skip files that already exist on disk, never worry about overwriting @@ -258,7 +254,7 @@ const formattingFunctions = (name) => { customOrDefaultTemplatePath({ side: 'web', generator: 'scaffold', - templatePath: path.join('lib', 'formattingFunctions.tsx.template'), + templatePath: path.join('lib', 'formatters.tsx.template'), }), { name, @@ -269,7 +265,7 @@ const formattingFunctions = (name) => { customOrDefaultTemplatePath({ side: 'web', generator: 'scaffold', - templatePath: path.join('lib', 'formattingFunctions.test.tsx.template'), + templatePath: path.join('lib', 'formatters.test.tsx.template'), }), { name, @@ -533,13 +529,13 @@ const componentFiles = async ( }) ) - const formattingFunctionsImports = columns + const formattersImports = columns .map((column) => column.displayFunction) .sort() .filter((v, i, a) => a.indexOf(v) === i) .join(', ') - const listFormattingFunctionsImports = columns + const listFormattersImports = columns .map((column) => column.listDisplayFunction) .sort() .filter((v, i, a) => a.indexOf(v) === i) @@ -576,8 +572,8 @@ const componentFiles = async ( idType, intForeignKeys, pascalScaffoldPath, - listFormattingFunctionsImports, - formattingFunctionsImports, + listFormattersImports, + formattersImports, ...templateStrings, } ) diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template index cd19dcebd812..3ea1e078bb90 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template @@ -3,7 +3,7 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { ${formattingFunctionsImports} } from 'src/lib/formattingFunctions' +import { ${formattersImports} } from 'src/lib/formatters' import type { Delete${singularPascalName}MutationVariables, Find${singularPascalName}ById } from 'types/graphql' diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template index 99eaeaac1858..161d817229c7 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template @@ -3,7 +3,7 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from '${importComponentNamesCell}' -import { ${listFormattingFunctionsImports} } from 'src/lib/formattingFunctions' +import { ${listFormattersImports} } from 'src/lib/formatters' import type { Delete${singularPascalName}MutationVariables, Find${pluralPascalName} } from 'types/graphql' diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template new file mode 100644 index 000000000000..d4e8dc5a19c8 --- /dev/null +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template @@ -0,0 +1,115 @@ +import { render, waitFor, screen } from '@redwoodjs/testing/web' + +import { + formatEnum, + jsonTruncate, + truncate, + timeTag, + checkboxInputTag, +} from './formatters' + +describe('formatEnum', () => { + it('handles nullish values', () => { + expect(formatEnum(null)).toEqual('') + expect(formatEnum('')).toEqual('') + expect(formatEnum(undefined)).toEqual('') + }) + + it('formats a list of values', () => { + expect( + formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET']) + ).toEqual('Red, Orange, Yellow, Green, Blue, Violet') + }) + + it('formats a single value', () => { + expect(formatEnum('DARK_BLUE')).toEqual('Dark blue') + }) + + it('returns an empty string for values of the wrong type (for JS projects)', () => { + // @ts-expect-error - Testing JS scenario + expect(formatEnum(5)).toEqual('') + }) +}) + +describe('truncate', () => { + it('truncates really long strings', () => { + expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000) + expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/) + }) + + it('does not modify short strings', () => { + expect(truncate('Short strinG')).toEqual('Short strinG') + }) + + it('adds ... to the end of truncated strings', () => { + expect(truncate('repeat'.repeat(1000))).toMatch(/\w\.\.\.$/) + }) + + it('accepts numbers', () => { + expect(truncate(123)).toEqual('123') + expect(truncate(0)).toEqual('0') + expect(truncate(0o000)).toEqual('0') + }) + + it('handles arguments of invalid type', () => { + // @ts-expect-error - Testing JS scenario + expect(truncate(false)).toEqual('false') + + expect(truncate(undefined)).toEqual('') + expect(truncate(null)).toEqual('') + }) +}) + +describe('jsonTruncate', () => { + it('truncates large json structures', () => { + expect( + jsonTruncate({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + kittens: 'kittens meow', + bazinga: 'Sheldon', + nested: { + foobar: 'I have no imagination', + two: 'Second nested item', + }, + five: 5, + bool: false, + }) + ).toMatch(/.+\n.+\w\.\.\.$/s) + }) +}) + +describe('timeTag', () => { + it('renders a date', async () => { + render(
{timeTag(new Date('1970-08-20').toUTCString())}
) + + await waitFor(() => screen.getByText(/1970.*00:00:00/)) + }) + + it('can take an empty input string', async () => { + expect(timeTag('')).toEqual('') + }) +}) + +describe('checkboxInputTag', () => { + it('can be checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeChecked() + }) + + it('can be unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).not.toBeChecked() + }) + + it('is disabled when checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) + + it('is disabled when unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) +}) diff --git a/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx b/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.tsx.template similarity index 89% rename from __fixtures__/test-project/web/src/lib/formattingFunctions.tsx rename to packages/cli/src/commands/generate/scaffold/templates/lib/formatters.tsx.template index 5761efb98df0..30552293578e 100644 --- a/__fixtures__/test-project/web/src/lib/formattingFunctions.tsx +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.tsx.template @@ -10,8 +10,8 @@ export const formatEnum = (values: string | string[] | null | undefined) => { if (Array.isArray(values)) { const humanizedValues = values.map((value) => humanize(value)) output = humanizedValues.join(', ') - } else if (values) { - output = humanize(values as string) + } else if (typeof values === 'string') { + output = humanize(values) } return output @@ -20,7 +20,7 @@ export const formatEnum = (values: string | string[] | null | undefined) => { export const truncate = (value: string | number) => { let output = value?.toString() ?? '' - if (output?.length > MAX_STRING_LENGTH) { + if (output.length > MAX_STRING_LENGTH) { output = output.substring(0, MAX_STRING_LENGTH) + '...' } From 84ec1121d5ba392b5c37553b70c6ef61a1fc9734 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 19 Sep 2022 11:25:57 +0200 Subject: [PATCH 16/22] Update snapshots --- .../__snapshots__/scaffold.test.js.snap | 130 +++++++++--------- .../__snapshots__/scaffoldNoNest.test.js.snap | 130 +++++++++--------- 2 files changed, 126 insertions(+), 134 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index a2c55cc4b0dd..b5fee5906c40 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -459,11 +459,7 @@ exports[`in javascript (default) mode creates a show component 1`] = ` import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { - checkboxInputTag, - jsonDisplay, - timeTag, -} from 'src/lib/formatters' +import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1284,43 +1280,43 @@ interface PostFormProps { const PostForm = (props: PostFormProps) => { const onSubmit = (data: FormPost) => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + props.onSave(data, props?.post?.id) } @@ -1333,7 +1329,7 @@ const PostForm = (props: PostFormProps) => { titleClassName="rw-form-error-title" listClassName="rw-form-error-list" /> - + - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1360,7 +1356,7 @@ const PostForm = (props: PostFormProps) => { > Slug - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1379,7 +1375,7 @@ const PostForm = (props: PostFormProps) => { > Author - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1398,7 +1394,7 @@ const PostForm = (props: PostFormProps) => { > Body - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1417,14 +1413,14 @@ const PostForm = (props: PostFormProps) => { > Image - + - + @@ -1435,14 +1431,14 @@ const PostForm = (props: PostFormProps) => { > Posted at - + - + @@ -1453,14 +1449,14 @@ const PostForm = (props: PostFormProps) => { > Is pinned - + - + @@ -1471,7 +1467,7 @@ const PostForm = (props: PostFormProps) => { > Read time - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true, required: true }} /> - + @@ -1490,7 +1486,7 @@ const PostForm = (props: PostFormProps) => { > Rating - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true }} /> - + @@ -1509,7 +1505,7 @@ const PostForm = (props: PostFormProps) => { > Upvotes - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1528,7 +1524,7 @@ const PostForm = (props: PostFormProps) => { > Metadata - + { errorClassName="rw-input rw-input-error" validation={{ valueAsJSON: true, required: true }} /> - + @@ -1547,7 +1543,7 @@ const PostForm = (props: PostFormProps) => { > Huge number - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap index 148e87f4b75b..83b11cce0492 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap @@ -415,11 +415,7 @@ exports[`in javascript (default) mode creates a show component 1`] = ` import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' -import { - checkboxInputTag, - jsonDisplay, - timeTag, -} from 'src/lib/formatters' +import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1240,43 +1236,43 @@ interface PostFormProps { const PostForm = (props: PostFormProps) => { const onSubmit = (data: FormPost) => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + props.onSave(data, props?.post?.id) } @@ -1289,7 +1285,7 @@ const PostForm = (props: PostFormProps) => { titleClassName="rw-form-error-title" listClassName="rw-form-error-list" /> - + - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1316,7 +1312,7 @@ const PostForm = (props: PostFormProps) => { > Slug - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1335,7 +1331,7 @@ const PostForm = (props: PostFormProps) => { > Author - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1354,7 +1350,7 @@ const PostForm = (props: PostFormProps) => { > Body - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1373,14 +1369,14 @@ const PostForm = (props: PostFormProps) => { > Image - + - + @@ -1391,14 +1387,14 @@ const PostForm = (props: PostFormProps) => { > Posted at - + - + @@ -1409,14 +1405,14 @@ const PostForm = (props: PostFormProps) => { > Is pinned - + - + @@ -1427,7 +1423,7 @@ const PostForm = (props: PostFormProps) => { > Read time - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true, required: true }} /> - + @@ -1446,7 +1442,7 @@ const PostForm = (props: PostFormProps) => { > Rating - + { errorClassName="rw-input rw-input-error" validation={{ valueAsNumber: true }} /> - + @@ -1465,7 +1461,7 @@ const PostForm = (props: PostFormProps) => { > Upvotes - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + @@ -1484,7 +1480,7 @@ const PostForm = (props: PostFormProps) => { > Metadata - + { errorClassName="rw-input rw-input-error" validation={{ valueAsJSON: true, required: true }} /> - + @@ -1503,7 +1499,7 @@ const PostForm = (props: PostFormProps) => { > Huge number - + { errorClassName="rw-input rw-input-error" validation={{ required: true }} /> - + From 7ca7551b7ffad800929534fd381afefd73c89551 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 Sep 2022 15:26:43 +0200 Subject: [PATCH 17/22] Better variable names --- packages/cli/src/commands/generate/scaffold/scaffold.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 876c04a753fd..2a0bb62fd586 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -532,13 +532,15 @@ const componentFiles = async ( const formattersImports = columns .map((column) => column.displayFunction) .sort() - .filter((v, i, a) => a.indexOf(v) === i) + // filter out duplicates, so we only keep unique import names + .filter((name, index, array) => array.indexOf(name) === index) .join(', ') const listFormattersImports = columns .map((column) => column.listDisplayFunction) .sort() - .filter((v, i, a) => a.indexOf(v) === i) + // filter out duplicates, so we only keep unique import names + .filter((name, index, array) => array.indexOf(name) === index) .join(', ') await asyncForEach(components, (component) => { From 7d9629c932e34c03384384b95077105461800560 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 Sep 2022 15:56:37 +0200 Subject: [PATCH 18/22] Add support for JS projects too, and better test coverage --- .../__snapshots__/scaffold.test.js.snap | 346 ++++++++++++++++++ .../scaffold/__tests__/scaffold.test.js | 31 ++ .../commands/generate/scaffold/scaffold.js | 12 +- 3 files changed, 385 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index b5fee5906c40..9f501b31203b 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -273,6 +273,179 @@ export default PostForm " `; +exports[`in javascript (default) mode creates a formatters function file 1`] = ` +"import React from 'react' + +import humanize from 'humanize-string' + +const MAX_STRING_LENGTH = 150 + +export const formatEnum = (values: string | string[] | null | undefined) => { + let output = '' + + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + output = humanizedValues.join(', ') + } else if (typeof values === 'string') { + output = humanize(values) + } + + return output +} + +export const truncate = (value: string | number) => { + let output = value?.toString() ?? '' + + if (output.length > MAX_STRING_LENGTH) { + output = output.substring(0, MAX_STRING_LENGTH) + '...' + } + + return output +} + +export const jsonTruncate = (obj: unknown) => { + return truncate(JSON.stringify(obj, null, 2)) +} + +export const timeTag = (dateTime?: string) => { + let output: string | JSX.Element = '' + + if (dateTime) { + output = ( + + ) + } + + return output +} + +export const checkboxInputTag = (checked: boolean) => { + return +} +" +`; + +exports[`in javascript (default) mode creates a formatters function test file 1`] = ` +"import { render, waitFor, screen } from '@redwoodjs/testing/web' + +import { + formatEnum, + jsonTruncate, + truncate, + timeTag, + checkboxInputTag, +} from './formatters' + +describe('formatEnum', () => { + it('handles nullish values', () => { + expect(formatEnum(null)).toEqual('') + expect(formatEnum('')).toEqual('') + expect(formatEnum(undefined)).toEqual('') + }) + + it('formats a list of values', () => { + expect( + formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET']) + ).toEqual('Red, Orange, Yellow, Green, Blue, Violet') + }) + + it('formats a single value', () => { + expect(formatEnum('DARK_BLUE')).toEqual('Dark blue') + }) + + it('returns an empty string for values of the wrong type (for JS projects)', () => { + // @ts-expect-error - Testing JS scenario + expect(formatEnum(5)).toEqual('') + }) +}) + +describe('truncate', () => { + it('truncates really long strings', () => { + expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000) + expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/) + }) + + it('does not modify short strings', () => { + expect(truncate('Short strinG')).toEqual('Short strinG') + }) + + it('adds ... to the end of truncated strings', () => { + expect(truncate('repeat'.repeat(1000))).toMatch(/\\w\\.\\.\\.$/) + }) + + it('accepts numbers', () => { + expect(truncate(123)).toEqual('123') + expect(truncate(0)).toEqual('0') + expect(truncate(0o000)).toEqual('0') + }) + + it('handles arguments of invalid type', () => { + // @ts-expect-error - Testing JS scenario + expect(truncate(false)).toEqual('false') + + expect(truncate(undefined)).toEqual('') + expect(truncate(null)).toEqual('') + }) +}) + +describe('jsonTruncate', () => { + it('truncates large json structures', () => { + expect( + jsonTruncate({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + kittens: 'kittens meow', + bazinga: 'Sheldon', + nested: { + foobar: 'I have no imagination', + two: 'Second nested item', + }, + five: 5, + bool: false, + }) + ).toMatch(/.+\\n.+\\w\\.\\.\\.$/s) + }) +}) + +describe('timeTag', () => { + it('renders a date', async () => { + render(
{timeTag(new Date('1970-08-20').toUTCString())}
) + + await waitFor(() => screen.getByText(/1970.*00:00:00/)) + }) + + it('can take an empty input string', async () => { + expect(timeTag('')).toEqual('') + }) +}) + +describe('checkboxInputTag', () => { + it('can be checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeChecked() + }) + + it('can be unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).not.toBeChecked() + }) + + it('is disabled when checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) + + it('is disabled when unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) +}) +" +`; + exports[`in javascript (default) mode creates a index page 1`] = ` "import PostsCell from 'src/components/Post/PostsCell' @@ -1572,6 +1745,179 @@ export default PostForm " `; +exports[`in typescript mode creates a formatters function file 1`] = ` +"import React from 'react' + +import humanize from 'humanize-string' + +const MAX_STRING_LENGTH = 150 + +export const formatEnum = (values: string | string[] | null | undefined) => { + let output = '' + + if (Array.isArray(values)) { + const humanizedValues = values.map((value) => humanize(value)) + output = humanizedValues.join(', ') + } else if (typeof values === 'string') { + output = humanize(values) + } + + return output +} + +export const truncate = (value: string | number) => { + let output = value?.toString() ?? '' + + if (output.length > MAX_STRING_LENGTH) { + output = output.substring(0, MAX_STRING_LENGTH) + '...' + } + + return output +} + +export const jsonTruncate = (obj: unknown) => { + return truncate(JSON.stringify(obj, null, 2)) +} + +export const timeTag = (dateTime?: string) => { + let output: string | JSX.Element = '' + + if (dateTime) { + output = ( + + ) + } + + return output +} + +export const checkboxInputTag = (checked: boolean) => { + return +} +" +`; + +exports[`in typescript mode creates a formatters function test file 1`] = ` +"import { render, waitFor, screen } from '@redwoodjs/testing/web' + +import { + formatEnum, + jsonTruncate, + truncate, + timeTag, + checkboxInputTag, +} from './formatters' + +describe('formatEnum', () => { + it('handles nullish values', () => { + expect(formatEnum(null)).toEqual('') + expect(formatEnum('')).toEqual('') + expect(formatEnum(undefined)).toEqual('') + }) + + it('formats a list of values', () => { + expect( + formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET']) + ).toEqual('Red, Orange, Yellow, Green, Blue, Violet') + }) + + it('formats a single value', () => { + expect(formatEnum('DARK_BLUE')).toEqual('Dark blue') + }) + + it('returns an empty string for values of the wrong type (for JS projects)', () => { + // @ts-expect-error - Testing JS scenario + expect(formatEnum(5)).toEqual('') + }) +}) + +describe('truncate', () => { + it('truncates really long strings', () => { + expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000) + expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/) + }) + + it('does not modify short strings', () => { + expect(truncate('Short strinG')).toEqual('Short strinG') + }) + + it('adds ... to the end of truncated strings', () => { + expect(truncate('repeat'.repeat(1000))).toMatch(/\\w\\.\\.\\.$/) + }) + + it('accepts numbers', () => { + expect(truncate(123)).toEqual('123') + expect(truncate(0)).toEqual('0') + expect(truncate(0o000)).toEqual('0') + }) + + it('handles arguments of invalid type', () => { + // @ts-expect-error - Testing JS scenario + expect(truncate(false)).toEqual('false') + + expect(truncate(undefined)).toEqual('') + expect(truncate(null)).toEqual('') + }) +}) + +describe('jsonTruncate', () => { + it('truncates large json structures', () => { + expect( + jsonTruncate({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + kittens: 'kittens meow', + bazinga: 'Sheldon', + nested: { + foobar: 'I have no imagination', + two: 'Second nested item', + }, + five: 5, + bool: false, + }) + ).toMatch(/.+\\n.+\\w\\.\\.\\.$/s) + }) +}) + +describe('timeTag', () => { + it('renders a date', async () => { + render(
{timeTag(new Date('1970-08-20').toUTCString())}
) + + await waitFor(() => screen.getByText(/1970.*00:00:00/)) + }) + + it('can take an empty input string', async () => { + expect(timeTag('')).toEqual('') + }) +}) + +describe('checkboxInputTag', () => { + it('can be checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeChecked() + }) + + it('can be unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).not.toBeChecked() + }) + + it('is disabled when checked', () => { + render(checkboxInputTag(true)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) + + it('is disabled when unchecked', () => { + render(checkboxInputTag(false)) + expect(screen.getByRole('checkbox')).toBeDisabled() + }) +}) +" +`; + exports[`in typescript mode creates a index page 1`] = ` "import PostsCell from 'src/components/Post/PostsCell' diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js index da2f369d65fd..f1f1c50b225d 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js @@ -392,6 +392,21 @@ describe('in javascript (default) mode', () => { ] ).toMatchSnapshot() }) + + // Formatters + + test('creates a formatters function file', () => { + console.log('files', files) + expect( + files[path.normalize('/path/to/project/web/src/lib/formatters.js')] + ).toMatchSnapshot() + }) + + test('creates a formatters function test file', () => { + expect( + files[path.normalize('/path/to/project/web/src/lib/formatters.test.js')] + ).toMatchSnapshot() + }) }) describe('in typescript mode', () => { @@ -680,6 +695,22 @@ describe('in typescript mode', () => { ] ).toMatchSnapshot() }) + + // Formatters + + test('creates a formatters function file', () => { + expect( + tsFiles[path.normalize('/path/to/project/web/src/lib/formatters.tsx')] + ).toMatchSnapshot() + }) + + test('creates a formatters function test file', () => { + expect( + tsFiles[ + path.normalize('/path/to/project/web/src/lib/formatters.test.tsx') + ] + ).toMatchSnapshot() + }) }) describe('tailwind flag', () => { diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 2a0bb62fd586..9d197098321b 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -181,7 +181,7 @@ export const files = async ({ typescript, })), ...assetFiles(name, tailwind), - ...formatters(name), + ...formatters(name, typescript), ...layoutFiles(name, pascalScaffoldPath, typescript, templateStrings), ...(await pageFiles( name, @@ -237,12 +237,16 @@ const assetFiles = (name, tailwind) => { return fileList } -const formatters = (name) => { - const outputPath = path.join(getPaths().web.src, 'lib', 'formatters.tsx') +const formatters = (name, isTypescript) => { + const outputPath = path.join( + getPaths().web.src, + 'lib', + isTypescript ? 'formatters.tsx' : 'formatters.js' + ) const outputPathTest = path.join( getPaths().web.src, 'lib', - 'formatters.test.tsx' + isTypescript ? 'formatters.test.tsx' : 'formatters.test.js' ) // skip files that already exist on disk, never worry about overwriting From 2b925339eb0aca8ef0bae675bda551479290be14 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 Sep 2022 23:34:44 +0200 Subject: [PATCH 19/22] Add humanize-string to rw app --- packages/cli/package.json | 1 - .../commands/destroy/scaffold/__tests__/scaffold.test.js | 1 + .../destroy/scaffold/__tests__/scaffoldNoNest.test.js | 1 + .../generate/scaffold/__tests__/editableColumns.test.js | 2 ++ .../generate/scaffold/__tests__/scaffold.test.js | 3 ++- .../generate/scaffold/__tests__/scaffoldNoNest.test.js | 2 ++ .../generate/scaffold/__tests__/scaffoldPath.test.js | 2 ++ .../scaffold/__tests__/scaffoldPathMulti.test.js | 2 ++ .../scaffold/__tests__/scaffoldPathMultiNoNest.test.js | 2 ++ .../scaffold/__tests__/scaffoldPathMultiword.test.js | 2 ++ .../__tests__/scaffoldPathMultiwordNoNest.test.js | 2 ++ .../scaffold/__tests__/scaffoldPathNoNest.test.js | 2 ++ packages/cli/src/commands/generate/scaffold/scaffold.js | 9 +++++++-- yarn.lock | 1 - 14 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 5cccd46f93ca..fcbe0e9f25b2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -48,7 +48,6 @@ "execa": "5.1.1", "fast-glob": "3.2.12", "fs-extra": "10.1.0", - "humanize-string": "2.1.0", "latest-version": "5.1.0", "listr": "0.14.3", "listr-verbose-renderer": "0.6.0", diff --git a/packages/cli/src/commands/destroy/scaffold/__tests__/scaffold.test.js b/packages/cli/src/commands/destroy/scaffold/__tests__/scaffold.test.js index 0945c6999d66..202c0fcf26fb 100644 --- a/packages/cli/src/commands/destroy/scaffold/__tests__/scaffold.test.js +++ b/packages/cli/src/commands/destroy/scaffold/__tests__/scaffold.test.js @@ -10,6 +10,7 @@ import { files } from '../../../generate/scaffold/scaffold' import { tasks } from '../scaffold' jest.mock('fs') +jest.mock('execa') jest.mock('../../../../lib', () => { return { diff --git a/packages/cli/src/commands/destroy/scaffold/__tests__/scaffoldNoNest.test.js b/packages/cli/src/commands/destroy/scaffold/__tests__/scaffoldNoNest.test.js index be37a5f654ee..c9a20893fa25 100644 --- a/packages/cli/src/commands/destroy/scaffold/__tests__/scaffoldNoNest.test.js +++ b/packages/cli/src/commands/destroy/scaffold/__tests__/scaffoldNoNest.test.js @@ -10,6 +10,7 @@ import { files } from '../../../generate/scaffold/scaffold' import { tasks } from '../scaffold' jest.mock('fs') +jest.mock('execa') jest.mock('../../../../lib', () => { return { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/editableColumns.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/editableColumns.test.js index 1802d4c172ce..4c2fe38fb634 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/editableColumns.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/editableColumns.test.js @@ -8,6 +8,8 @@ import { getDefaultArgs } from '../../../../lib' import { yargsDefaults as defaults } from '../../../generate' import * as scaffold from '../scaffold' +jest.mock('execa') + describe('editable columns', () => { let files let form diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js index f1f1c50b225d..2d4f02aae367 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffold.test.js @@ -8,6 +8,8 @@ import { getDefaultArgs } from '../../../../lib' import { yargsDefaults as defaults } from '../../../generate' import * as scaffold from '../scaffold' +jest.mock('execa') + describe('in javascript (default) mode', () => { let files @@ -396,7 +398,6 @@ describe('in javascript (default) mode', () => { // Formatters test('creates a formatters function file', () => { - console.log('files', files) expect( files[path.normalize('/path/to/project/web/src/lib/formatters.js')] ).toMatchSnapshot() diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js index 39f72be1741f..46cf81af5c4c 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldNoNest.test.js @@ -8,6 +8,8 @@ import { getDefaultArgs } from '../../../../lib' import { yargsDefaults as defaults } from '../../../generate' import * as scaffold from '../scaffold' +jest.mock('execa') + describe('in javascript (default) mode', () => { let files diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js index 70cb76fc7363..91ee10f7b5da 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPath.test.js @@ -5,6 +5,8 @@ import '../../../../lib/test' import * as scaffold from '../scaffold' +jest.mock('execa') + let filesLower, filesUpper beforeAll(async () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js index b8d14a806859..ffd39f0427b6 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMulti.test.js @@ -5,6 +5,8 @@ import '../../../../lib/test' import * as scaffold from '../scaffold' +jest.mock('execa') + let filesNestedLower, filesNestedUpper beforeAll(async () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js index a5e77f991c9d..6f9b0c0997b3 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiNoNest.test.js @@ -5,6 +5,8 @@ import '../../../../lib/test' import * as scaffold from '../scaffold' +jest.mock('execa') + let filesNestedLower, filesNestedUpper beforeAll(async () => { diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js index 420fbf36ffc3..50fc75e00075 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiword.test.js @@ -5,6 +5,8 @@ import '../../../../lib/test' import * as scaffold from '../scaffold' +jest.mock('execa') + let filesMultiwordUpper let filesMultiwordDash let filesMultiwordUnderscore diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js index eeb81e76ccc4..f44a5eea6666 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathMultiwordNoNest.test.js @@ -5,6 +5,8 @@ import '../../../../lib/test' import * as scaffold from '../scaffold' +jest.mock('execa') + let filesMultiwordUpper let filesMultiwordDash let filesMultiwordUnderscore diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js index 0ac66f8da818..5f19e1b188b1 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldPathNoNest.test.js @@ -5,6 +5,8 @@ import '../../../../lib/test' import * as scaffold from '../scaffold' +jest.mock('execa') + let filesLower, filesUpper beforeAll(async () => { diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 9d197098321b..f093a4191889 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -2,6 +2,7 @@ import fs from 'fs' import path from 'path' import camelcase from 'camelcase' +import execa from 'execa' import humanize from 'humanize-string' import Listr from 'listr' import { paramCase } from 'param-case' @@ -181,7 +182,7 @@ export const files = async ({ typescript, })), ...assetFiles(name, tailwind), - ...formatters(name, typescript), + ...(await formatters(name, typescript)), ...layoutFiles(name, pascalScaffoldPath, typescript, templateStrings), ...(await pageFiles( name, @@ -237,7 +238,7 @@ const assetFiles = (name, tailwind) => { return fileList } -const formatters = (name, isTypescript) => { +const formatters = async (name, isTypescript) => { const outputPath = path.join( getPaths().web.src, 'lib', @@ -254,6 +255,10 @@ const formatters = (name, isTypescript) => { return } + // Has to be v2.1.0 because v3 switched to cjs module format, which we don't + // support yet (2022-09-20) + await execa('yarn', ['workspace', 'web', 'add', 'humanize-string@2.1.0']) + const template = generateTemplate( customOrDefaultTemplatePath({ side: 'web', diff --git a/yarn.lock b/yarn.lock index 3880e0f67cdc..c9e2de79418e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6263,7 +6263,6 @@ __metadata: execa: 5.1.1 fast-glob: 3.2.12 fs-extra: 10.1.0 - humanize-string: 2.1.0 jest: 29.0.3 latest-version: 5.1.0 listr: 0.14.3 From b34071ce9e350a52dfede26ddde6223c59b06d8f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 21 Sep 2022 06:12:28 +0200 Subject: [PATCH 20/22] Separate task for installing helper packages --- .../commands/generate/scaffold/scaffold.js | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index f093a4191889..19f5ea57d83d 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -255,10 +255,6 @@ const formatters = async (name, isTypescript) => { return } - // Has to be v2.1.0 because v3 switched to cjs module format, which we don't - // support yet (2022-09-20) - await execa('yarn', ['workspace', 'web', 'add', 'humanize-string@2.1.0']) - const template = generateTemplate( customOrDefaultTemplatePath({ side: 'web', @@ -665,6 +661,25 @@ const addLayoutImport = ({ model: name, path: scaffoldPath = '' }) => { } } +const addHelperPackages = async (isTypescript) => { + const outputPath = path.join( + getPaths().web.src, + 'lib', + isTypescript ? 'formatters.tsx' : 'formatters.js' + ) + + // If the formatters file already exists, the helper packages are most likely + // already installed + if (fs.existsSync(outputPath)) { + return + } + + // Has to be v2.1.0 because v3 switched to ESM module format, which we don't + // support yet (2022-09-20) + // TODO: Update to latest version when RW supports ESMs + await execa('yarn', ['workspace', 'web', 'add', 'humanize-string@2.1.0']) +} + const addSetImport = (task) => { const routesPath = getPaths().web.routes const routesContent = readFile(routesPath).toString() @@ -763,6 +778,10 @@ export const tasks = ({ return writeFilesTask(f, { overwriteExisting: force }) }, }, + { + title: 'Install helper packages', + task: () => addHelperPackages(typescript), + }, { title: 'Adding layout import...', task: async () => addLayoutImport({ model, path }), From 6ef72d116c552eb35920debf51008b43d1baa8d9 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 21 Sep 2022 07:20:47 +0200 Subject: [PATCH 21/22] Check package.json for installed package --- .../src/commands/generate/scaffold/scaffold.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 19f5ea57d83d..e19c21f13c86 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -661,17 +661,13 @@ const addLayoutImport = ({ model: name, path: scaffoldPath = '' }) => { } } -const addHelperPackages = async (isTypescript) => { - const outputPath = path.join( - getPaths().web.src, - 'lib', - isTypescript ? 'formatters.tsx' : 'formatters.js' - ) +const addHelperPackages = async (task) => { + const packageJsonPath = path.join(getPaths().web.base, 'package.json') + const packageJson = require(packageJsonPath) - // If the formatters file already exists, the helper packages are most likely - // already installed - if (fs.existsSync(outputPath)) { - return + // Skip if humanize-string is already installed + if (packageJson.dependencies['humanize-string']) { + return task.skip('Skipping. Already installed') } // Has to be v2.1.0 because v3 switched to ESM module format, which we don't @@ -780,7 +776,7 @@ export const tasks = ({ }, { title: 'Install helper packages', - task: () => addHelperPackages(typescript), + task: (_, task) => addHelperPackages(task), }, { title: 'Adding layout import...', From 5586e10f9464dc9f0b86c8b8c2aabff786d736af Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 21 Sep 2022 11:05:22 +0200 Subject: [PATCH 22/22] TransformTSToJS --- .../__tests__/__snapshots__/scaffold.test.js.snap | 13 +++++++------ .../cli/src/commands/generate/scaffold/scaffold.js | 8 ++++++-- packages/cli/src/lib/index.js | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index 9f501b31203b..dd519db77e7c 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -280,7 +280,7 @@ import humanize from 'humanize-string' const MAX_STRING_LENGTH = 150 -export const formatEnum = (values: string | string[] | null | undefined) => { +export const formatEnum = (values) => { let output = '' if (Array.isArray(values)) { @@ -293,7 +293,7 @@ export const formatEnum = (values: string | string[] | null | undefined) => { return output } -export const truncate = (value: string | number) => { +export const truncate = (value) => { let output = value?.toString() ?? '' if (output.length > MAX_STRING_LENGTH) { @@ -303,12 +303,12 @@ export const truncate = (value: string | number) => { return output } -export const jsonTruncate = (obj: unknown) => { +export const jsonTruncate = (obj) => { return truncate(JSON.stringify(obj, null, 2)) } -export const timeTag = (dateTime?: string) => { - let output: string | JSX.Element = '' +export const timeTag = (dateTime) => { + let output = '' if (dateTime) { output = ( @@ -321,7 +321,7 @@ export const timeTag = (dateTime?: string) => { return output } -export const checkboxInputTag = (checked: boolean) => { +export const checkboxInputTag = (checked) => { return } " @@ -403,6 +403,7 @@ describe('jsonTruncate', () => { foobar: 'I have no imagination', two: 'Second nested item', }, + five: 5, bool: false, }) diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index e19c21f13c86..d42355de33ac 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -278,8 +278,12 @@ const formatters = async (name, isTypescript) => { ) return { - [outputPath]: template, - [outputPathTest]: templateTest, + [outputPath]: isTypescript + ? template + : transformTSToJS(outputPath, template), + [outputPathTest]: isTypescript + ? templateTest + : transformTSToJS(outputPathTest, templateTest), } } diff --git a/packages/cli/src/lib/index.js b/packages/cli/src/lib/index.js index d33cb44c71f0..0e54679b6ba4 100644 --- a/packages/cli/src/lib/index.js +++ b/packages/cli/src/lib/index.js @@ -244,7 +244,7 @@ export const transformTSToJS = (filename, content) => { retainLines: true, }) - return prettify(filename.replace(/\.ts$/, '.js'), code) + return prettify(filename.replace(/\.tsx?$/, '.js'), code) } /**