Skip to content

Commit 6d81283

Browse files
author
LB
authoredOct 21, 2020
fix: Update plugin schema testing util and associated tests (#27574)
* Fix default * validate matching options, add associated test * fix error test * it's only the mismatch that matters * remove test, the utility can't handle external calls yet * use when instead of async * remove keys now that we're not using external * fix typescript * fix expected errors in tests * missed some tests
1 parent dc0ce0c commit 6d81283

File tree

11 files changed

+94
-73
lines changed

11 files changed

+94
-73
lines changed
 

‎packages/gatsby-plugin-mdx/__tests__/gatsby-node.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ import { testPluginOptionsSchema } from "gatsby-plugin-utils"
22
import { pluginOptionsSchema } from "../gatsby-node"
33

44
describe(`pluginOptionsSchema`, () => {
5-
it(`should provide meaningful errors when fields are invalid`, () => {
5+
it(`should provide meaningful errors when fields are invalid`, async () => {
66
const expectedErrors = [
7-
`"extensions" "[0]" must be a string. "[1]" must be a string. "[2]" must be a string`,
7+
`"extensions[0]" must be a string`,
8+
`"extensions[1]" must be a string`,
9+
`"extensions[2]" must be a string`,
810
`"defaultLayout" must be of type object`,
9-
`"gatsbyRemarkPlugins" "[0]" does not match any of the allowed types. "[1]" does not match any of the allowed types`,
11+
`"gatsbyRemarkPlugins[0]" does not match any of the allowed types`,
12+
`"gatsbyRemarkPlugins[1]" does not match any of the allowed types`,
1013
`"remarkPlugins" must be an array`,
1114
`"rehypePlugins" must be an array`,
12-
`"mediaTypes" "[0]" must be a string. "[1]" must be a string`,
15+
`"mediaTypes[0]" must be a string`,
16+
`"mediaTypes[1]" must be a string`,
1317
`"shouldBlockNodeFromTransformation" must have an arity lesser or equal to 1`,
1418
]
1519

16-
const { errors } = testPluginOptionsSchema(pluginOptionsSchema, {
20+
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
1721
extensions: [1, 2, 3],
1822
defaultLayout: `this should be an object`,
1923
gatsbyRemarkPlugins: [1, { not: `existing prop` }, `valid one`],
@@ -26,8 +30,8 @@ describe(`pluginOptionsSchema`, () => {
2630
expect(errors).toEqual(expectedErrors)
2731
})
2832

29-
it(`should validate the schema`, () => {
30-
const { isValid } = testPluginOptionsSchema(pluginOptionsSchema, {
33+
it(`should validate the schema`, async () => {
34+
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
3135
extensions: [`.mdx`, `.mdxx`],
3236
defaultLayout: {
3337
posts: `../post-layout.js`,

‎packages/gatsby-plugin-netlify/src/__tests__/gatsby-node.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { testPluginOptionsSchema } from "gatsby-plugin-utils"
22
import { pluginOptionsSchema } from "../gatsby-node"
33

44
describe(`gatsby-node.js`, () => {
5-
it(`should provide meaningful errors when fields are invalid`, () => {
5+
it(`should provide meaningful errors when fields are invalid`, async () => {
66
const expectedErrors = [
77
`"headers" must be of type object`,
88
`"allPageHeaders" must be an array`,
@@ -13,7 +13,7 @@ describe(`gatsby-node.js`, () => {
1313
`"generateMatchPathRewrites" must be a boolean`,
1414
]
1515

16-
const { errors } = testPluginOptionsSchema(pluginOptionsSchema, {
16+
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
1717
headers: `this should be an object`,
1818
allPageHeaders: `this should be an array`,
1919
mergeSecurityHeaders: `this should be a boolean`,
@@ -26,8 +26,8 @@ describe(`gatsby-node.js`, () => {
2626
expect(errors).toEqual(expectedErrors)
2727
})
2828

29-
it(`should validate the schema`, () => {
30-
const { isValid } = testPluginOptionsSchema(pluginOptionsSchema, {
29+
it(`should validate the schema`, async () => {
30+
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
3131
headers: {
3232
"/some-page": [`Bearer: Some-Magic-Token`],
3333
"/some-other-page": [`some`, `great`, `headers`],

‎packages/gatsby-plugin-offline/src/__tests__/gatsby-node.js

+20-6
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,29 @@ describe(`onPostBuild`, () => {
111111
})
112112

113113
describe(`pluginOptionsSchema`, () => {
114-
it(`should provide meaningful errors when fields are invalid`, () => {
114+
it(`should provide meaningful errors when fields are invalid`, async () => {
115115
const expectedErrors = [
116-
`"precachePages" "[0]" must be a string. "[1]" must be a string. "[2]" must be a string`,
116+
`"precachePages[0]" must be a string`,
117+
`"precachePages[1]" must be a string`,
118+
`"precachePages[2]" must be a string`,
117119
`"appendScript" must be a string`,
118120
`"debug" must be a boolean`,
119-
`"workboxConfig" "importWorkboxFrom" must be a string. "globDirectory" must be a string. "globPatterns[0]" must be a string. "globPatterns[1]" must be a string. "globPatterns[2]" must be a string. "modifyURLPrefix./" must be a string. "cacheId" must be a string. "dontCacheBustURLsMatching" must be of type object. "runtimeCaching[0].handler" must be one of [StaleWhileRevalidate, CacheFirst, NetworkFirst, NetworkOnly, CacheOnly]. "runtimeCaching[1]" must be of type object. "runtimeCaching[2]" must be of type object. "skipWaiting" must be a boolean. "clientsClaim" must be a boolean`,
121+
`"workboxConfig.importWorkboxFrom" must be a string`,
122+
`"workboxConfig.globDirectory" must be a string`,
123+
`"workboxConfig.globPatterns[0]" must be a string`,
124+
`"workboxConfig.globPatterns[1]" must be a string`,
125+
`"workboxConfig.globPatterns[2]" must be a string`,
126+
`"workboxConfig.modifyURLPrefix./" must be a string`,
127+
`"workboxConfig.cacheId" must be a string`,
128+
`"workboxConfig.dontCacheBustURLsMatching" must be of type object`,
129+
`"workboxConfig.runtimeCaching[0].handler" must be one of [StaleWhileRevalidate, CacheFirst, NetworkFirst, NetworkOnly, CacheOnly]`,
130+
`"workboxConfig.runtimeCaching[1]" must be of type object`,
131+
`"workboxConfig.runtimeCaching[2]" must be of type object`,
132+
`"workboxConfig.skipWaiting" must be a boolean`,
133+
`"workboxConfig.clientsClaim" must be a boolean`,
120134
]
121135

122-
const { errors } = testPluginOptionsSchema(pluginOptionsSchema, {
136+
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
123137
precachePages: [1, 2, 3],
124138
appendScript: 1223,
125139
debug: `This should be a boolean`,
@@ -148,8 +162,8 @@ describe(`pluginOptionsSchema`, () => {
148162
expect(errors).toEqual(expectedErrors)
149163
})
150164

151-
it(`should validate the schema`, () => {
152-
const { isValid } = testPluginOptionsSchema(pluginOptionsSchema, {
165+
it(`should validate the schema`, async () => {
166+
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
153167
precachePages: [`/about-us/`, `/projects/*`],
154168
appendScript: `src/custom-sw-code.js`,
155169
debug: true,

‎packages/gatsby-plugin-sass/src/__tests__/gatsby-node.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe(`gatsby-plugin-sass`, () => {
5858
})
5959

6060
describe(`pluginOptionsSchema`, () => {
61-
it(`should provide meaningful errors when fields are invalid`, () => {
61+
it(`should provide meaningful errors when fields are invalid`, async () => {
6262
const expectedErrors = [
6363
`"implementation" must be of type object`,
6464
`"postCssPlugins" must be an array`,
@@ -85,7 +85,7 @@ describe(`pluginOptionsSchema`, () => {
8585
`"sourceMapRoot" must be a string`,
8686
]
8787

88-
const { errors } = testPluginOptionsSchema(pluginOptionsSchema, {
88+
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
8989
implementation: `This should be a require() thing`,
9090
postCssPlugins: `This should be an array of postCss plugins`,
9191
sassRuleTest: `This should be a regexp`,
@@ -114,8 +114,8 @@ describe(`pluginOptionsSchema`, () => {
114114
expect(errors).toEqual(expectedErrors)
115115
})
116116

117-
it(`should validate the schema`, () => {
118-
const { isValid } = testPluginOptionsSchema(pluginOptionsSchema, {
117+
it(`should validate the schema`, async () => {
118+
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
119119
implementation: require(`../gatsby-node.js`),
120120
postCssPlugins: [{ post: `CSS plugin` }],
121121
sassRuleTest: /\.global\.s(a|c)ss$/,

‎packages/gatsby-plugin-typescript/src/__tests__/gatsby-node.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ describe(`gatsby-plugin-typescript`, () => {
7474
})
7575

7676
describe(`plugin schema`, () => {
77-
it(`should provide meaningful errors when fields are invalid`, () => {
77+
it(`should provide meaningful errors when fields are invalid`, async () => {
7878
const expectedErrors = [
7979
`"isTSX" must be a boolean`,
8080
`"jsxPragma" must be a string`,
8181
`"allExtensions" must be a boolean`,
8282
]
8383

84-
const { errors } = testPluginOptionsSchema(pluginOptionsSchema, {
84+
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
8585
isTSX: `this should be a boolean`,
8686
jsxPragma: 123,
8787
allExtensions: `this should be a boolean`,
@@ -90,14 +90,24 @@ describe(`gatsby-plugin-typescript`, () => {
9090
expect(errors).toEqual(expectedErrors)
9191
})
9292

93-
it(`should validate the schema`, () => {
94-
const { isValid } = testPluginOptionsSchema(pluginOptionsSchema, {
93+
it(`should validate the schema`, async () => {
94+
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
9595
isTSX: false,
9696
jsxPragma: `ReactFunction`,
9797
allExtensions: false,
9898
})
9999

100100
expect(isValid).toBe(true)
101101
})
102+
103+
it(`should break when isTSX doesn't match allExtensions`, async () => {
104+
const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, {
105+
isTSX: true,
106+
jsxPragma: `ReactFunction`,
107+
allExtensions: false,
108+
})
109+
110+
expect(errors).toEqual([`"allExtensions" must be [true]`])
111+
})
102112
})
103113
})

‎packages/gatsby-plugin-typescript/src/gatsby-node.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ if (process.env.GATSBY_EXPERIMENTAL_PLUGIN_OPTION_VALIDATION) {
4646
.default(`React`),
4747
allExtensions: Joi.boolean()
4848
.description(`Indicates that every file should be parsed as TS or TSX.`)
49-
.default(false),
49+
.default(false)
50+
.when(`isTSX`, { is: true, then: Joi.valid(true) }),
5051
})
5152
}
5253

‎packages/gatsby-plugin-utils/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ Utility to validate and test plugin options schemas. An example of a plugin opti
2828
// This is an example using Jest (https://jestjs.io/)
2929
import { testPluginOptionsSchema } from "gatsby-plugin-utils"
3030

31-
it(`should partially validate one value of a schema`, () => {
31+
it(`should partially validate one value of a schema`, async () => {
3232
const pluginSchema = ({ Joi }) =>
3333
Joi.object({
3434
someOtherValue: Joi.string()
3535
toVerify: Joi.boolean(),
3636
})
3737

3838
// Only the "toVerify" key of the schema will be verified in this test
39-
const { isValid, errors } = testPluginOptionsSchema(pluginSchema, {
39+
const { isValid, errors } = await testPluginOptionsSchema(pluginSchema, {
4040
toVerify: `abcd`,
4141
})
4242

‎packages/gatsby-plugin-utils/src/__tests__/test-plugin-options-schema.ts

+17-13
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { testPluginOptionsSchema } from "../test-plugin-options-schema"
22
import { ObjectSchema } from "../joi"
33

44
describe(`testPluginOptionsSchema`, () => {
5-
it(`should partially validate one value of a schema`, () => {
5+
it(`should partially validate one value of a schema`, async () => {
66
const pluginSchema = ({ Joi }): ObjectSchema =>
77
Joi.object({
88
str: Joi.string(),
99
nb: Joi.number(),
1010
toVerify: Joi.boolean(),
1111
})
1212

13-
const { isValid, errors } = testPluginOptionsSchema(pluginSchema, {
13+
const { isValid, errors } = await testPluginOptionsSchema(pluginSchema, {
1414
toVerify: `abcd`,
1515
})
1616

@@ -22,29 +22,29 @@ describe(`testPluginOptionsSchema`, () => {
2222
`)
2323
})
2424

25-
it(`should partially validate multiples value of a schema`, () => {
25+
it(`should partially validate multiples value of a schema`, async () => {
2626
const pluginSchema = ({ Joi }): ObjectSchema =>
2727
Joi.object({
2828
str: Joi.string(),
2929
nb: Joi.number(),
3030
toVerify: Joi.boolean(),
3131
})
3232

33-
const { isValid, errors } = testPluginOptionsSchema(pluginSchema, {
33+
const { isValid, errors } = await testPluginOptionsSchema(pluginSchema, {
3434
toVerify: `abcd`,
3535
nb: `invalid value`,
3636
})
3737

3838
expect(isValid).toBe(false)
3939
expect(errors).toMatchInlineSnapshot(`
4040
Array [
41-
"\\"toVerify\\" must be a boolean",
4241
"\\"nb\\" must be a number",
42+
"\\"toVerify\\" must be a boolean",
4343
]
4444
`)
4545
})
4646

47-
it(`should validate half of a real world plugin schema`, () => {
47+
it(`should validate half of a real world plugin schema`, async () => {
4848
const pluginSchema = ({ Joi }): ObjectSchema =>
4949
Joi.object({
5050
trackingId: Joi.string()
@@ -85,7 +85,7 @@ describe(`testPluginOptionsSchema`, () => {
8585
cookieDomain: Joi.string(),
8686
})
8787

88-
const { isValid, errors } = testPluginOptionsSchema(pluginSchema, {
88+
const { isValid, errors } = await testPluginOptionsSchema(pluginSchema, {
8989
trackingId: undefined,
9090
head: `invalid boolean value`,
9191
anonymize: `invalid boolean value`,
@@ -100,12 +100,14 @@ describe(`testPluginOptionsSchema`, () => {
100100
"\\"head\\" must be a boolean",
101101
"\\"anonymize\\" must be a boolean",
102102
"\\"respectDNT\\" must be a boolean",
103-
"\\"exclude\\" \\"[0]\\" must be a string. \\"[1]\\" must be a string. \\"[2]\\" must be a string",
103+
"\\"exclude[0]\\" must be a string",
104+
"\\"exclude[1]\\" must be a string",
105+
"\\"exclude[2]\\" must be a string",
104106
]
105107
`)
106108
})
107109

108-
it(`should validate an entire real world plugin schema`, () => {
110+
it(`should validate an entire real world plugin schema`, async () => {
109111
const pluginSchema = ({ Joi }): ObjectSchema =>
110112
Joi.object({
111113
trackingId: Joi.string()
@@ -146,7 +148,7 @@ describe(`testPluginOptionsSchema`, () => {
146148
cookieDomain: Joi.string(),
147149
})
148150

149-
const { isValid, errors } = testPluginOptionsSchema(pluginSchema, {
151+
const { isValid, errors } = await testPluginOptionsSchema(pluginSchema, {
150152
trackingId: undefined,
151153
head: `invalid boolean value`,
152154
anonymize: `invalid boolean value`,
@@ -169,7 +171,9 @@ describe(`testPluginOptionsSchema`, () => {
169171
"\\"head\\" must be a boolean",
170172
"\\"anonymize\\" must be a boolean",
171173
"\\"respectDNT\\" must be a boolean",
172-
"\\"exclude\\" \\"[0]\\" must be a string. \\"[1]\\" must be a string. \\"[2]\\" must be a string",
174+
"\\"exclude[0]\\" must be a string",
175+
"\\"exclude[1]\\" must be a string",
176+
"\\"exclude[2]\\" must be a string",
173177
"\\"pageTransitionDelay\\" must be a number",
174178
"\\"optimizeId\\" must be a string",
175179
"\\"experimentId\\" must be a string",
@@ -182,13 +186,13 @@ describe(`testPluginOptionsSchema`, () => {
182186
`)
183187
})
184188

185-
it(`should check the validity of a schema`, () => {
189+
it(`should check the validity of a schema`, async () => {
186190
const pluginSchema = ({ Joi }): ObjectSchema =>
187191
Joi.object({
188192
toVerify: Joi.boolean(),
189193
})
190194

191-
const { isValid, errors } = testPluginOptionsSchema(pluginSchema, {
195+
const { isValid, errors } = await testPluginOptionsSchema(pluginSchema, {
192196
toVerify: false,
193197
})
194198

Original file line numberDiff line numberDiff line change
@@ -1,37 +1,25 @@
11
import { Joi } from "./joi"
22
import { GatsbyNode } from "gatsby"
3+
import { validateOptionsSchema } from "./validate"
4+
import { IPluginInfoOptions } from "gatsby"
35

46
interface ITestPluginOptionsSchemaReturnType {
57
errors: Array<string>
68
isValid: boolean
79
}
810

9-
export function testPluginOptionsSchema<PluginOptions = object>(
11+
export async function testPluginOptionsSchema(
1012
pluginSchemaFunction: Exclude<GatsbyNode["pluginOptionsSchema"], undefined>,
11-
pluginOptions: PluginOptions
12-
): ITestPluginOptionsSchemaReturnType {
13-
const pluginOptionsNames = Object.keys(pluginOptions)
13+
pluginOptions: IPluginInfoOptions
14+
): Promise<ITestPluginOptionsSchemaReturnType> {
1415
const pluginSchema = pluginSchemaFunction({ Joi })
15-
const errors: Array<string> = []
1616

17-
pluginOptionsNames.forEach(pluginOptionName => {
18-
const partialSchema = pluginSchema.extract(pluginOptionName)
19-
const { error } = partialSchema.validate(pluginOptions[pluginOptionName], {
20-
abortEarly: false,
21-
})
17+
try {
18+
await validateOptionsSchema(pluginSchema, pluginOptions)
19+
} catch (e) {
20+
const errors = e.details.map(detail => detail.message)
21+
return { isValid: false, errors }
22+
}
2223

23-
if (error) {
24-
const errorMessage = error.message
25-
26-
// In the case of an array, "value" does not exist in the error message
27-
// and so we can't replace it with the plugin option name, we have to concat it
28-
const message = errorMessage.includes(`"value"`)
29-
? errorMessage.replace(`"value"`, `"${pluginOptionName}"`)
30-
: `"${pluginOptionName}" ${errorMessage}`
31-
32-
errors.push(message)
33-
}
34-
})
35-
36-
return { isValid: errors.length === 0, errors }
24+
return { isValid: true, errors: [] }
3725
}

0 commit comments

Comments
 (0)
Please sign in to comment.