Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix(gatsby-remark-images): allow tracedSVG to accept object with settings #28242

Merged
merged 2 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 208 additions & 1 deletion packages/gatsby-remark-images/src/__tests__/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { testPluginOptionsSchema } from "gatsby-plugin-utils"
import { pluginOptionsSchema } from "../gatsby-node"
import { Potrace } from "potrace"

describe(`pluginOptionsSchema`, () => {
it(`should provide meaningful errors when fields are invalid`, async () => {
Expand All @@ -13,7 +14,7 @@ describe(`pluginOptionsSchema`, () => {
`"backgroundColor" must be a string`,
`"quality" must be a number`,
`"withWebp" must be one of [object, boolean]`,
`"tracedSVG" must be a boolean`,
`"tracedSVG" must be one of [boolean, object]`,
`"loading" must be one of [lazy, eager, auto]`,
`"disableBgImageOnAlpha" must be a boolean`,
`"disableBgImage" must be a boolean`,
Expand Down Expand Up @@ -102,4 +103,210 @@ describe(`pluginOptionsSchema`, () => {
expect(isValid).toBe(false)
})
})

describe(`allow different variants of "tracedSVG" option`, () => {
describe(`supports boolean variant`, () => {
it.each([
[`true`, true],
[`false`, false],
])(`%s`, async (_title, booleanValue) => {
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
tracedSVG: booleanValue,
})

expect(isValid).toBe(true)
})
})

describe(`supports object notation`, () => {
it(`should validate when all fields are set`, async () => {
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
tracedSVG: {
turnPolicy: Potrace.TURNPOLICY_RIGHT,
turdSize: 50,
alphaMax: 0.5,
optCurve: false,
optTolerance: 0.9,
threshold: 230,
blackOnWhite: false,
color: `red`,
background: `green`,
},
})

expect(isValid).toBe(true)
})

it(`should validate when some fields are set`, async () => {
const { isValid } = await testPluginOptionsSchema(pluginOptionsSchema, {
tracedSVG: {
turnPolicy: Potrace.TURNPOLICY_RIGHT,
turdSize: 50,
// alphaMax: 0.5,
// optCurve: 0.2,
// optTolerance: 0.9,
// threshold: 230,
// blackOnWhite: false,
color: `red`,
background: `green`,
},
})

expect(isValid).toBe(true)
})

it(`should fail validation when unknown fields are set`, async () => {
const { isValid, errors } = await testPluginOptionsSchema(
pluginOptionsSchema,
{
tracedSVG: {
foo: `bar`,
},
}
)

expect(isValid).toBe(false)
expect(errors).toMatchInlineSnapshot(`
Array [
"\\"tracedSVG.foo\\" is not allowed",
]
`)
})

describe(`turnPolicy variants`, () => {
it.each([
`TURNPOLICY_BLACK`,
`TURNPOLICY_WHITE`,
`TURNPOLICY_LEFT`,
`TURNPOLICY_RIGHT`,
`TURNPOLICY_MINORITY`,
`TURNPOLICY_MAJORITY`,
])(`supports "%s" by policy name`, async name => {
const { isValid } = await testPluginOptionsSchema(
pluginOptionsSchema,
{
tracedSVG: { turnPolicy: Potrace[name] },
pieh marked this conversation as resolved.
Show resolved Hide resolved
}
)

expect(isValid).toBe(true)
})

it.each([
Potrace.TURNPOLICY_BLACK,
Potrace.TURNPOLICY_WHITE,
Potrace.TURNPOLICY_LEFT,
Potrace.TURNPOLICY_RIGHT,
Potrace.TURNPOLICY_MINORITY,
Potrace.TURNPOLICY_MAJORITY,
])(`supports "%s" by policy value`, async value => {
const { isValid } = await testPluginOptionsSchema(
pluginOptionsSchema,
{
tracedSVG: { turnPolicy: value },
}
)

expect(isValid).toBe(true)
})

it(`Doesn't support arbitrary string values`, async () => {
const { isValid, errors } = await testPluginOptionsSchema(
pluginOptionsSchema,
{
tracedSVG: { turnPolicy: `foo` },
}
)

expect(isValid).toBe(false)
expect(errors).toMatchInlineSnapshot(`
Array [
"\\"tracedSVG.turnPolicy\\" must be one of [TURNPOLICY_BLACK, TURNPOLICY_WHITE, TURNPOLICY_LEFT, TURNPOLICY_RIGHT, TURNPOLICY_MINORITY, TURNPOLICY_MAJORITY, black, white, left, right, minority, majority]",
]
`)
})
})

describe(`threshold`, () => {
// valid settings
it.each([
[
`THRESHOLD_AUTO`,
{
value: Potrace.THRESHOLD_AUTO,
expectedIsValid: true,
},
],
[
0,
{
expectedIsValid: true,
},
],
[
128,
{
expectedIsValid: true,
},
],
[
255,
{
expectedIsValid: true,
},
],
])(`Allow setting %s`, async (titleAndMaybeValue, { value }) => {
if (typeof value === `undefined`) {
// if value wasn't explicitly set use title
value = titleAndMaybeValue
}

const { isValid } = await testPluginOptionsSchema(
pluginOptionsSchema,
{
tracedSVG: { threshold: value },
}
)

expect(isValid).toBe(true)
})

// invalid settings
it.each([
[
-5,
{
expectedIsValid: false,
errorMessage: `"tracedSVG.threshold" must be greater than or equal to 0`,
},
],
[
256,
{
expectedIsValid: false,
errorMessage: `"tracedSVG.threshold" must be less than or equal to 255`,
},
],
])(
`Doesn't allow setting %s`,
async (titleAndMaybeValue, { value, errorMessage }) => {
if (typeof value === `undefined`) {
// if value wasn't explicitly set use title
value = titleAndMaybeValue
}

const { isValid, errors } = await testPluginOptionsSchema(
pluginOptionsSchema,
{
tracedSVG: { threshold: value },
}
)

expect(isValid).toBe(false)
expect(errors[0]).toEqual(errorMessage)
}
)
})
})
})
})
40 changes: 39 additions & 1 deletion packages/gatsby-remark-images/src/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { Potrace } = require(`potrace`)

exports.pluginOptionsSchema = function ({ Joi }) {
return Joi.object({
maxWidth: Joi.number()
Expand Down Expand Up @@ -52,7 +54,43 @@ exports.pluginOptionsSchema = function ({ Joi }) {
.description(
`Additionally generate WebP versions alongside your chosen file format. They are added as a srcset with the appropriate mimetype and will be loaded in browsers that support the format. Pass true for default support, or an object of options to specifically override those for the WebP files. For example, pass { quality: 80 } to have the WebP images be at quality level 80.`
),
tracedSVG: Joi.boolean()
tracedSVG: Joi.alternatives()
.try(
Joi.boolean(),
Joi.object({
turnPolicy: Joi.string()
.valid(
// this plugin also allow to use key names and not exact values
`TURNPOLICY_BLACK`,
`TURNPOLICY_WHITE`,
`TURNPOLICY_LEFT`,
`TURNPOLICY_RIGHT`,
`TURNPOLICY_MINORITY`,
`TURNPOLICY_MAJORITY`,
// it also allow using actual policy values
Potrace.TURNPOLICY_BLACK,
Potrace.TURNPOLICY_WHITE,
Potrace.TURNPOLICY_LEFT,
Potrace.TURNPOLICY_RIGHT,
Potrace.TURNPOLICY_MINORITY,
Potrace.TURNPOLICY_MAJORITY
Comment on lines +63 to +76
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 ways of setting it is covered by

// Translate Potrace constants (e.g. TURNPOLICY_LEFT, COLOR_AUTO) to the values Potrace expects
const { Potrace } = require(`potrace`)
const argsKeys = Object.keys(args)
args = argsKeys.reduce((result, key) => {
const value = args[key]
result[key] = Potrace.hasOwnProperty(value) ? Potrace[value] : value
return result
}, {})

)
.default(Potrace.TURNPOLICY_MAJORITY),
turdSize: Joi.number().default(100),
alphaMax: Joi.number(),
optCurve: Joi.boolean().default(true),
optTolerance: Joi.number().default(0.4),
threshold: Joi.alternatives()
.try(
Joi.number().min(0).max(255),
Joi.number().valid(Potrace.THRESHOLD_AUTO)
)
.default(Potrace.THRESHOLD_AUTO),
blackOnWhite: Joi.boolean().default(true),
color: Joi.string().default(`lightgray`),
background: Joi.string().default(`transparent`),
})
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defaults and some of extra restrictions are coming from:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also - maybe it's safer to remove defaults, because it's not this plugin that set them. My main motivation to add those defaults is that maybe they are (or will be) displayed in some form - if they are not, then better remove them as they can get out of sync

.default(false)
.description(
`Use traced SVGs for placeholder images instead of the “blur up” effect. Pass true for traced SVGs with the default settings (seen here), or an object of options to override the default. For example, pass { color: "#F00", turnPolicy: "TURNPOLICY_MAJORITY" } to change the color of the trace to red and the turn policy to TURNPOLICY_MAJORITY. See node-potrace parameter documentation for a full listing and explanation of the available options.`
Expand Down