orphic-cypress
Version:
Set of utilities and typescript transformers to cover storybook stories with cypress component tests
323 lines (287 loc) • 10.2 kB
text/typescript
import { dedent } from "ts-dedent";
import {
removeSkips,
transformSource,
TransformSourceOptions,
} from "./story-code";
describe("story-code", () => {
describe("removeSkips", () => {
it("should remove lines skipped with @skip", () => {
const lines = [
"const notSkipped = 1;",
"const skipped = 1; // story-code @skip",
"const notSkipped2 = 2;",
"// story-code @skip", // test blank line
// test extra whitespace
"const skipped2 = 2; // story-code @skip",
];
expect(removeSkips(lines)).to.deep.equal([
"const notSkipped = 1;",
"const notSkipped2 = 2;",
]);
});
it("should remove next lines skipped with @skip-next", () => {
const lines = [
"const notSkipped = 1;",
"// story-code @skip-next",
"const skipped = 1;",
"const notSkipped2 = 2;",
"// story-code @skip-next",
"// story-code @skip-next", // test repeated
"const skipped2 = 2;",
"// story-code @skip-next",
// test interaction with skip
"const alreadySkipped = 3 // story-code @skip",
// same line still skips
"// story-code @skip-next @skip",
"const skipped3 = 3;",
];
expect(removeSkips(lines)).to.deep.equal([
"const notSkipped = 1;",
"const notSkipped2 = 2;",
]);
});
it("should remove skip sections", () => {
const lines = [
"const notSkipped = 1;",
"// story-code @skip-start",
"const skipped = 1;",
"// story-code @skip-end",
"const notSkipped2 = 2;",
"// story-code @skip-start",
"const skipped2 = 2;",
"const skipped3 = 3;",
"// story-code @skip-end",
];
expect(removeSkips(lines)).to.deep.equal([
"const notSkipped = 1;",
"const notSkipped2 = 2;",
]);
});
it("should remove skip sections inclusive of commented line", () => {
const lines = [
"const notSkipped = 1;",
"const skipped = 1; // story-code @skip-start",
"const skipped2 = 2;",
"const skipped3 = 3; // story-code @skip-end",
"const notSkipped2 = 2;",
];
expect(removeSkips(lines)).to.deep.equal([
"const notSkipped = 1;",
"const notSkipped2 = 2;",
]);
});
it("should ignore dupe starts and skip lines with skip-end regardless", () => {
const lines = [
"const notSkipped = 1;",
"const skipped = 1; // story-code @skip-start",
"const skipped2 = 2; // story-code @skip-start",
"const skipped3 = 3;",
"const skipped4 = 4; // story-code @skip-end",
"const skipped5 = 5; // story-code @skip-end",
"const notSkipped2 = 2;",
];
expect(removeSkips(lines)).to.deep.equal([
"const notSkipped = 1;",
"const notSkipped2 = 2;",
]);
});
it("should override @skip with regions, but only if in that order", () => {
// so really just: 'some wonkiness ensues if you combine'
const lines = [
"const notSkipped = 1;",
"const skipped = 1; // story-code @skip-start @skip",
"const skipped3 = 3;",
"const skipped4 = 4; // story-code @skip-end",
"const notSkipped2 = 2;",
"const skipped = 1; // story-code @skip @skip-start",
"const skipped3 = 3;",
"const skipped4 = 4; // story-code @skip-end",
];
expect(removeSkips(lines)).to.deep.equal([
"const notSkipped = 1;",
"const notSkipped2 = 2;",
"const skipped3 = 3;",
]);
});
});
describe("transformSource", () => {
const basicTest = (source: string, startLine: number, endLine: number) =>
transformSource()("<SomeComponent prop={1} />", {
id: "somedir-nested--some-story",
originalStoryFn: { name: "SomeStory" },
parameters: {
storySource: {
source,
locationsMap: {
"some-story": {
startLoc: { line: startLine },
endLoc: { line: endLine },
},
},
},
},
} as any);
const basicFile = (include?: "default" | "start") => dedent`
import type { ComponentStory } from "@storybook/react";
import { SomeComponent } from "./";
export default { component: SomeComponent };
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
// ${include ? "story-code @include-" + include : "not-a-directive"}
SomeStory.args = { prop: 1 };
export const OtherStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} prop={1} />
);
OtherStory.args = { prop2: 2 };
`;
it("should gather start and end locations as provided by storysource addon", () => {
const result = basicTest(basicFile(), 6, 8);
const expected = dedent`
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
`;
expect(result).to.equal(expected);
});
it("should include default line as a single line", () => {
const result = basicTest(basicFile("default"), 6, 8);
const expected = dedent`
export default { component: SomeComponent };
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
`;
expect(result).to.equal(expected);
});
it("should gather and include default lines", () => {
const result = basicTest(
dedent`
import type { ComponentStory } from "@storybook/react";
import { SomeComponent } from "./";
export default {
component: SomeComponent,
title: "SomeDir/Nested",
};
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
// story-code @include-default
SomeStory.args = { prop: 1 };
export const OtherStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} prop={1} />
);
OtherStory.args = { prop2: 2 };
`,
9,
11
);
const expected = dedent`
export default {
component: SomeComponent,
title: "SomeDir/Nested",
};
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
`;
expect(result).to.equal(expected);
});
it("should gather and include start lines", () => {
const result = basicTest(
dedent`
import type { ComponentStory } from "@storybook/react";
import { SomeComponent } from "./";
const somethingElseImportant = 1;
export default {
component: SomeComponent,
title: "SomeDir/Nested",
};
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
// story-code @include-start
SomeStory.args = { prop: 1 };
export const OtherStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} prop={1} />
);
OtherStory.args = { prop2: 2 };
`,
11,
13
);
const expected = dedent`
import type { ComponentStory } from "@storybook/react";
import { SomeComponent } from "./";
const somethingElseImportant = 1;
export default {
component: SomeComponent,
title: "SomeDir/Nested",
};
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
`;
expect(result).to.equal(expected);
});
it("should return the snippet if anything goes wrong", () => {
const expected = "<SomeComponent prop={1} />";
expect(basicTest(basicFile(), 1, 1000)).to.equal(
expected,
"end line too high"
);
expect(basicTest(basicFile(), 1000, 1001)).to.equal(
expected,
"start line too high"
);
});
describe("object file syntax", () => {
const source = dedent`
import type { ComponentStory, ComponentStoryObj } from "@storybook/react";
import { SomeComponent } from "./";
export default {
component: SomeComponent,
title: "SomeDir/Nested",
};
export const SomeStory: ComponentStory<typeof SomeStory> = (args) => (
<SomeComponent {...args} />
);
// story-code @include-start
SomeStory.args = { prop: 1 };
export const OtherStoryObj: ComponentStoryObj<typeof SomeStory> = {
<SomeComponent {...args} prop={1} />
};
OtherStoryObj.args = { prop2: 2 };
// story-code @end
`;
const objTest = (opts: TransformSourceOptions) =>
transformSource(opts)("<OtherStoryObj prop={1} />", {
id: "somedir-nested--other-story-obj",
originalStoryFn: { name: null },
name: "Other Story Obj",
parameters: {
storySource: {
source,
locationsMap: {
// note: missing other-story-obj
"some-story": { startLoc: { line: 9 }, endLoc: { line: 15 } },
},
},
},
} as any);
it("should get location information from objects despite storysource not generating it", () => {
const expected = dedent`\n
export const OtherStoryObj: ComponentStoryObj<typeof SomeStory> = {
<SomeComponent {...args} prop={1} />
};
OtherStoryObj.args = { prop2: 2 };
`;
expect(objTest({ includeObjects: true })).to.equal(expected);
});
it("should get the snippet for objects if includeObjects is not true", () => {
expect(objTest({})).to.equal("<OtherStoryObj prop={1} />");
});
});
});
});