From 38b5e80b1292bdd41a0829fb0ee4a8ef508e060f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 4 Oct 2022 00:25:28 +0200 Subject: [PATCH] Support Decimal in scenario tests (#6491) Co-authored-by: Dominic Saadi --- .../src/commands/generate/service/service.js | 27 ++++++++++ .../service/templates/test.ts.template | 53 +++++++------------ 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/packages/cli/src/commands/generate/service/service.js b/packages/cli/src/commands/generate/service/service.js index 1844174e8af4..c32239679524 100644 --- a/packages/cli/src/commands/generate/service/service.js +++ b/packages/cli/src/commands/generate/service/service.js @@ -154,6 +154,29 @@ export const buildStringifiedScenario = async (model) => { }) } +export const fieldTypes = async (model) => { + const { scalarFields } = await parseSchema(model) + + // Example value + // { + // name: 'score', + // kind: 'scalar', + // isList: false, + // isRequired: true, + // isUnique: false, + // isId: false, + // isReadOnly: false, + // hasDefaultValue: false, + // type: 'Int', + // isGenerated: false, + // isUpdatedAt: false + // } + return scalarFields.reduce((acc, value) => { + acc[value.name] = value.type + return acc + }, {}) +} + // outputs fields necessary to create an object in the test file export const fieldsToInput = async (model) => { const { scalarFields, foreignKeys } = await parseSchema(model) @@ -285,6 +308,10 @@ export const files = async ({ relations: relations || [], create: await fieldsToInput(model), update: await fieldsToUpdate(model), + types: await fieldTypes(model), + prismaImport: (await parseSchema(model)).scalarFields.some( + (field) => field.type === 'Decimal' + ), prismaModel: model, ...rest, }, diff --git a/packages/cli/src/commands/generate/service/templates/test.ts.template b/packages/cli/src/commands/generate/service/templates/test.ts.template index d8f757306eaa..1869099c4040 100644 --- a/packages/cli/src/commands/generate/service/templates/test.ts.template +++ b/packages/cli/src/commands/generate/service/templates/test.ts.template @@ -1,13 +1,20 @@ -<% // Extracts the values of an object by converting the JSON into a string. - // Looks for quoted strings, either by single (') ro double (") quotes. +<% // Transforms an object or single value into something that's more suitable + // for generating test cases + // If a `type` is passed in, the string for creating an object of that type + // will be generated + // If no type, or a type we don't support, is passed in we'll default to + // generating regular strings + // Looks for quoted strings, either by single (') or double (") quotes. // When found // - Removes the quotes around `scenario` variables. // - Removes the quotes around `BigInt` fields. - // It can also return object instances via the `options` parameter - // - `options.useDateObjects` default value: `false` - // - false: will return date values as a string. ex: "2022-09-01T00:21:58.876Z" - // - true: will return date values as a date. ex: `new Date("2022-09-01T00:21:58.876Z")` - const extractValues = (obj, options={ useDateObjects: false }) => { + const transformValue = (obj, type) => { + if (type === 'DateTime') { + return `new Date('${obj.toISOString()}')` + } else if (type === 'Decimal') { + return `new Prisma.Decimal(${obj})` + } + return JSON.stringify(obj).replace(/['"].*?['"]/g, (string) => { if (string.match(/scenario\./)) { return string.replace(/['"]/g, '') @@ -18,30 +25,10 @@ return string.substr(1, string.length - 2) } - if (options.useDateObjects) { - // Creates a new Date declaration if the string matches this ISO8601 date format. - // ex: "2022-09-01T00:21:58.876Z" will return `new Date("2022-09-01T00:21:58.876Z")` - if (string.match(/^\"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\"$/)) { - return `new Date(${string})` - } - } - return string }) - } - - // This is a thin wrapper that allows us to call the main function (extractValues) - // with an options parameter. We need this wrapper because object parameters {...} - // are not supported in template evaluations such as: - // - ${extractExpectedValue(value, {useDateObjects:true})} - // If we add a closing curly bracket they will be interpreted as the closing - // bracket of the evaluation and throw an error when generating the service: `Unexpected token ')'` - // ex: `${extractExpectedValue(value, { useDateObjects: true } )}`. - // ^---> Opening bracket Closing bracket <---^ ^---> Unexpected token ')' - const extractExpectedValue = (obj) => { - return extractValues(obj, { useDateObjects: true }) } %> -import type { ${prismaModel} } from '@prisma/client' +<% if (prismaImport) { %>import { Prisma, ${prismaModel} } from '@prisma/client'<% } else { %>import type { ${prismaModel} } from '@prisma/client'<% } %> import { ${pluralCamelName}<% if (crud) { %>,${singularCamelName}, create${singularPascalName}, update${singularPascalName}, delete${singularPascalName}<% } %> } from './${pluralCamelName}' import type { StandardScenario } from './${pluralCamelName}.scenarios' @@ -65,24 +52,24 @@ describe('${pluralCamelName}', () => { expect(result).toEqual(scenario.${singularCamelName}.one) }) - <% if (create) { %>scenario('creates a ${singularCamelName}', async (${extractValues(create).includes('scenario.') ? 'scenario: StandardScenario' : ''}) => { + <% if (create) { %>scenario('creates a ${singularCamelName}', async (${transformValue(create).includes('scenario.') ? 'scenario: StandardScenario' : ''}) => { const result = await create${singularPascalName}({ - input: ${extractValues(create)}, + input: ${transformValue(create)}, }) <% for (const [name, value] of Object.entries(create)) { %> - expect(result.${name}).toEqual(${extractExpectedValue(value)})<% } %> + expect(result.${name}).toEqual(${transformValue(value, types[name])})<% } %> })<% } %> <% if (update) { %>scenario('updates a ${singularCamelName}', async (scenario: StandardScenario) => {<% rand = parseInt(Math.random() * 10000000) %> const original = await (${singularCamelName}({ id: scenario.${singularCamelName}.one.id })) as ${prismaModel} const result = await update${singularPascalName}({ id: original.id, - input: ${extractValues(update)}, + input: ${transformValue(update)}, }) <% for (const [name, value] of Object.entries(update)) { %> - expect(result.${name}).toEqual(${extractExpectedValue(value)})<% } %> + expect(result.${name}).toEqual(${transformValue(value, types[name])})<% } %> })<% } %> scenario('deletes a ${singularCamelName}', async (scenario: StandardScenario) => {