Skip to content

Commit

Permalink
Support Decimal in scenario tests (#6491)
Browse files Browse the repository at this point in the history
Co-authored-by: Dominic Saadi <dominiceliassaadi@gmail.com>
  • Loading branch information
Tobbe and jtoar committed Oct 3, 2022
1 parent 7f45da9 commit 38b5e80
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 33 deletions.
27 changes: 27 additions & 0 deletions packages/cli/src/commands/generate/service/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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, '')
Expand All @@ -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'
Expand All @@ -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) => {
Expand Down

0 comments on commit 38b5e80

Please sign in to comment.