Skip to content

Commit

Permalink
Add ValidSettingsKey check for schema
Browse files Browse the repository at this point in the history
  • Loading branch information
aswamy committed Jan 29, 2025
1 parent 8372166 commit b696ba6
Show file tree
Hide file tree
Showing 6 changed files with 463 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .changeset/forty-moose-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@shopify/theme-check-common': minor
'@shopify/theme-check-node': minor
---

Theme check verifies if setting key exists in schema

- Check if the keys inside `presets.[].settings` and `default.settings` exist as `settings.[].id` in the same file
- Check if the keys inside `presets.[](recursive .blocks.[]).settings` and `default.blocks.[].settings` exist as `settings.[].id` inside the referenced block's file
2 changes: 2 additions & 0 deletions packages/theme-check-common/src/checks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { ValidJSON } from './valid-json';
import { ValidLocalBlocks } from './valid-local-blocks';
import { ValidSchema } from './valid-schema';
import { ValidSchemaName } from './valid-schema-name';
import { ValidSettingsKey } from './valid-settings-key';
import { ValidStaticBlockType } from './valid-static-block-type';
import { VariableName } from './variable-name';
import { AppBlockMissingSchema } from './app-block-missing-schema';
Expand Down Expand Up @@ -91,6 +92,7 @@ export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [
ValidJSON,
ValidLocalBlocks,
ValidSchema,
ValidSettingsKey,
ValidStaticBlockType,
VariableName,
ValidSchemaName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
import { expect, describe, it } from 'vitest';
import { ValidSettingsKey } from './index';
import { check } from '../../test';

describe('Moduel: ValidSettingsKey', () => {
const schemaTemplate = {
name: 'Example',
settings: [
{
id: 'example-text-setting',
type: 'text',
label: 'Example Text Setting',
},
],
};

describe('default settings', () => {
it('does not report an error when default setting exists', async () => {
const theme = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
default: {
settings: {
'example-text-setting': 'value',
},
},
}),
};

const offenses = await check(theme, [ValidSettingsKey]);
expect(offenses).to.have.length(0);
});

it('reports an error when default setting does not exist', async () => {
const theme = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
default: {
settings: {
'non-existent-setting': 'value',
},
},
}),
};

const offenses = await check(theme, [ValidSettingsKey]);
expect(offenses).to.have.length(1);
expect(offenses[0].message).to.equal(
`Setting 'non-existent-setting' does not exist in schema.`,
);
});

it('does not report an error when default settings are defined in a block schema', async () => {
const theme = {
'blocks/example.liquid': toLiquidFile({
...schemaTemplate,
default: {
settings: {
'non-existent-setting': 'value',
},
},
}),
};

const offenses = await check(theme, [ValidSettingsKey]);
expect(offenses).to.have.length(0);
});
});

describe('presets settings', () => {
it('does not report an error when presets setting exists', async () => {
const theme = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
presets: [
{
settings: {
'example-text-setting': 'value',
},
},
],
}),
};

const offenses = await check(theme, [ValidSettingsKey]);
expect(offenses).to.have.length(0);
});

it('reports an error when presets setting does not exist', async () => {
const theme = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
presets: [
{
settings: {
'non-existent-setting-1': 'value',
},
},
{
settings: {
'non-existent-setting-2': 'value',
},
},
],
}),
};

const offenses = await check(theme, [ValidSettingsKey]);
expect(offenses).to.have.length(2);
expect(offenses[0].message).to.equal(
`Setting 'non-existent-setting-1' does not exist in schema.`,
);
expect(offenses[1].message).to.equal(
`Setting 'non-existent-setting-2' does not exist in schema.`,
);
});
});

const tests = [
{
label: 'default',
blockTemplate: (blocks: any[]) => {
return {
default: {
blocks,
},
};
},
},
{
label: 'presets',
blockTemplate: (blocks: any[]) => {
return {
presets: [
{
blocks,
},
],
};
},
},
];

tests.forEach(({ label, blockTemplate }) => {
describe(`${label} block settings`, () => {
describe('referenced blocks', () => {
const referencedBlock = {
'blocks/referenced.liquid': toLiquidFile(schemaTemplate),
};

it(`does not report an error when ${label} block setting exists in referenced file`, async () => {
const fileToTest = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
...blockTemplate([
{
type: 'referenced',
settings: {
'example-text-setting': 'value',
},
},
]),
}),
};

const offenses = await check(
{
...referencedBlock,
...fileToTest,
},
[ValidSettingsKey],
);
expect(offenses).to.have.length(0);
});

it('does not report an error when referenced file does not exist', async () => {
const fileToTest = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
...blockTemplate([
{
type: 'non-existent-file',
settings: {
'non-existent-setting': 'value',
},
},
]),
}),
};

const offenses = await check(
{
...referencedBlock,
...fileToTest,
},
[ValidSettingsKey],
);
expect(offenses).to.have.length(0);
});

it(`reports an error when ${label} block setting does not exist in referenced file`, async () => {
const fileToTest = {
'sections/example.liquid': toLiquidFile({
...schemaTemplate,
...blockTemplate([
{
type: 'referenced',
settings: {
'non-existent-setting': 'value',
},
},
]),
}),
};

const offenses = await check(
{
...referencedBlock,
...fileToTest,
},
[ValidSettingsKey],
);
expect(offenses).to.have.length(1);
expect(offenses[0].message).to.equal(
`Setting 'non-existent-setting' does not exist in 'blocks/referenced.liquid'.`,
);
});
});

describe('local blocks', () => {
const localBlocksTemplate = {
blocks: [
{
type: 'local-block',
name: 'Local block',
settings: [
{
id: 'local-setting',
type: 'text',
label: 'Local Setting',
},
],
},
],
};

it(`reports an error when ${label} block setting does not exist in existing local block`, async () => {
const fileToTest = {
'sections/example.liquid': toLiquidFile({
...localBlocksTemplate,
...blockTemplate([
{
type: 'local-block',
settings: {
'non-existent-setting': 'value',
},
},
]),
}),
};

const offenses = await check(fileToTest, [ValidSettingsKey]);
expect(offenses).to.have.length(1);
expect(offenses[0].message).to.equal(
`Setting 'non-existent-setting' does not exist in schema.`,
);
});

it(`does not report an error when ${label} block setting does not exist in non-existent local block`, async () => {
const fileToTest = {
'sections/example.liquid': toLiquidFile({
...localBlocksTemplate,
...blockTemplate([
{
type: 'non-existent-local-block',
settings: {
'non-existent-setting': 'value',
},
},
]),
}),
};

const offenses = await check(fileToTest, [ValidSettingsKey]);
expect(offenses).to.have.length(0);
});

it(`does not report an error when ${label} block setting exists in local block`, async () => {
const fileToTest = {
'sections/example.liquid': toLiquidFile({
...localBlocksTemplate,
...blockTemplate([
{
type: 'local-block',
settings: {
'local-setting': 'value',
},
},
]),
}),
};

const offenses = await check(fileToTest, [ValidSettingsKey]);
expect(offenses).to.have.length(0);
});
});
});
});
});

function toLiquidFile(content: any) {
return `
{% schema %}
${JSON.stringify(content)}
{% endschema %}
`;
}
Loading

0 comments on commit b696ba6

Please sign in to comment.