Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcelh1983 committed Feb 13, 2025
2 parents d3c68fe + 3e5b681 commit 5040077
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 15 deletions.
4 changes: 4 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { setWcStorybookHelpersConfig } from 'wc-storybook-helpers';
import { withActions } from '@storybook/addon-actions/decorator';
import prettier from 'prettier-v2'; /* https://github.com/storybookjs/storybook/issues/8078#issuecomment-2325332120 */
import HTMLParser from 'prettier-v2/parser-html'; /* https://github.com/storybookjs/storybook/issues/8078#issuecomment-2325332120 */
import { expect } from '@storybook/test';

import customElements from '../custom-elements.json';
import { customViewports } from './custom-viewport-sizes';
import DocumentationTemplate from './DocumentationTemplate.mdx';
import { toBePositionedRelativeTo } from '../test/setup/toBePositionedRelativeTo';

import type { Preview } from '@storybook/web-components';

Expand All @@ -27,6 +29,8 @@ setWcStorybookHelpersConfig({
renderDefaultValues: false
});

expect.extend({ toBePositionedRelativeTo });

setCustomElementsManifest(customElements);

const preview: Preview = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { html, TemplateInstance } from 'lit';
import { getWcStorybookHelpers } from 'wc-storybook-helpers';
import { expect, fireEvent, fn, waitFor, within } from '@storybook/test';
import { expect, fireEvent, fn, waitFor } from '@storybook/test';
import { getByShadowRole } from 'shadow-dom-testing-library';
import { findByShadowTitle, getByShadowText, within } from 'shadow-dom-testing-library';

import type { QtiSimpleChoice } from '../qti-simple-choice';
import type { Meta, StoryObj } from '@storybook/web-components';
Expand Down Expand Up @@ -50,20 +51,38 @@ export const ChoiceLabelDecimal: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-decimal'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('1')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('2')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('3')).toBeTruthy();
}
};

export const ChoiceLabelLowerAlpha: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-lower-alpha'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('a')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('b')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('c')).toBeTruthy();
}
};

export const ChoiceLabelUpperAlpha: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-upper-alpha'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('A')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('B')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('C')).toBeTruthy();
}
};

Expand All @@ -82,13 +101,25 @@ export const ChoiceLabelSuffixPeriod: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-suffix-period'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('A.')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('B.')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('C.')).toBeTruthy();
}
};

export const ChoiceLabelSuffixParenthesis: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-suffix-parenthesis'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('A)')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('B)')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('C)')).toBeTruthy();
}
};

Expand All @@ -100,13 +131,25 @@ export const ChoiceLabelSuffixAlphaParenthesis: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-lower-alpha qti-labels-suffix-parenthesis'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('a)')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('b)')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('c)')).toBeTruthy();
}
};

export const ChoiceLabelSuffixDecimalPeriod: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-labels-decimal qti-labels-suffix-period'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByShadowText<QtiSimpleChoice>('1.')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('2.')).toBeTruthy();
expect(canvas.getByShadowText<QtiSimpleChoice>('3.')).toBeTruthy();
}
};

Expand All @@ -118,13 +161,29 @@ export const ChoiceOrientationVertical: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-orientation-vertical'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const ChoiceA = canvas.getByText<QtiSimpleChoice>('You must stay with your luggage at all times.');
const ChoiceB = canvas.getByText<QtiSimpleChoice>('Do not let someone else look after your luggage.');
const ChoiceC = canvas.getByText<QtiSimpleChoice>('Remember your luggage when you leave.');
expect(ChoiceB).toBePositionedRelativeTo(ChoiceA, 'below');
expect(ChoiceC).toBePositionedRelativeTo(ChoiceB, 'below');
}
};

export const ChoiceOrientationHorizontal: Story = {
render: TemplateThreeOptions,
args: {
class: 'qti-orientation-horizontal'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const ChoiceA = canvas.getByText<QtiSimpleChoice>('You must stay with your luggage at all times.');
const ChoiceB = canvas.getByText<QtiSimpleChoice>('Do not let someone else look after your luggage.');
const ChoiceC = canvas.getByText<QtiSimpleChoice>('Remember your luggage when you leave.');
expect(ChoiceB).toBePositionedRelativeTo(ChoiceA, 'right');
expect(ChoiceC).toBePositionedRelativeTo(ChoiceB, 'right');
}
};

Expand All @@ -147,13 +206,41 @@ export const ChoiceStacking1: Story = {
render: args => TemplateSixOptions({ ...args, 'max-choices': '0' }),
args: {
class: 'qti-choices-stacking-1'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const hydrogenChoice = canvas.getByText<QtiSimpleChoice>('Hydrogen');
const heliumChoice = canvas.getByText<QtiSimpleChoice>('Helium');
const carbonChoice = canvas.getByText<QtiSimpleChoice>('Carbon');
const oxygenChoice = canvas.getByText<QtiSimpleChoice>('Oxygen');
const nitrogenChoice = canvas.getByText<QtiSimpleChoice>('Nitrogen');
const chlorineChoice = canvas.getByText<QtiSimpleChoice>('Chlorine');
expect(heliumChoice).toBePositionedRelativeTo(hydrogenChoice, 'below');
expect(carbonChoice).toBePositionedRelativeTo(hydrogenChoice, 'below');
expect(oxygenChoice).toBePositionedRelativeTo(carbonChoice, 'below');
expect(nitrogenChoice).toBePositionedRelativeTo(oxygenChoice, 'below');
expect(chlorineChoice).toBePositionedRelativeTo(nitrogenChoice, 'below');
}
};

export const ChoiceStacking2: Story = {
render: args => TemplateSixOptions({ ...args, 'max-choices': '0' }),
args: {
class: 'qti-choices-stacking-2'
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const hydrogenChoice = canvas.getByText<QtiSimpleChoice>('Hydrogen');
const heliumChoice = canvas.getByText<QtiSimpleChoice>('Helium');
const carbonChoice = canvas.getByText<QtiSimpleChoice>('Carbon');
const oxygenChoice = canvas.getByText<QtiSimpleChoice>('Oxygen');
const nitrogenChoice = canvas.getByText<QtiSimpleChoice>('Nitrogen');
const chlorineChoice = canvas.getByText<QtiSimpleChoice>('Chlorine');
expect(heliumChoice).toBePositionedRelativeTo(hydrogenChoice, 'right');
expect(carbonChoice).toBePositionedRelativeTo(hydrogenChoice, 'below');
expect(oxygenChoice).toBePositionedRelativeTo(carbonChoice, 'right');
expect(nitrogenChoice).toBePositionedRelativeTo(oxygenChoice, 'left');
expect(chlorineChoice).toBePositionedRelativeTo(nitrogenChoice, 'right');
}
};

Expand Down Expand Up @@ -184,31 +271,27 @@ export const ChoiceStacking5: Story = {

export const ChoiceOrientationStackingH3: Story = {
render: args => TemplateSixOptions({ ...args, 'max-choices': '0' }),

args: {
class: 'qti-choices-stacking-3 qti-orientation-horizontal'
}
};

export const ChoiceOrientationStackingV3: Story = {
render: args => TemplateSixOptions({ ...args, 'max-choices': '0' }),

args: {
class: 'qti-choices-stacking-3 qti-orientation-vertical'
}
};

export const ChoiceOrientationStackingV2: Story = {
render: TemplateThreeOptions,

args: {
class: 'qti-choices-stacking-2 qti-orientation-vertical'
}
};

export const ChoiceOrientationStackingH2: Story = {
render: TemplateThreeOptions,

args: {
class: 'qti-choices-stacking-2 qti-orientation-horizontal'
}
Expand Down
19 changes: 10 additions & 9 deletions test/setup/customMatchers.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { XMLBuilder, XMLParser } from 'fast-xml-parser';
import { expect } from 'vitest';

expect.extend({
export const customMatchers = {
toEqualXml(received, expected) {
const parser = new XMLParser({
ignoreAttributes: false,
trimValues: true,
trimValues: true
});

const builder = new XMLBuilder({
ignoreAttributes: false,
format: true, // Prettify the output for readability
format: true // Prettify the output for readability
});

const receivedObj = parser.parse(received);
Expand All @@ -21,18 +21,19 @@ expect.extend({
if (pass) {
return {
message: () => `expected XML not to be equal`,
pass: true,
pass: true
};
} else {
// Convert JSON objects back to XML strings
const receivedXml = builder.build(receivedObj);
const expectedXml = builder.build(expectedObj);

return {
message: () =>
`Expected XML structures to be equal:\n\nReceived:\n${receivedXml}\n\nExpected:\n${expectedXml}`,
pass: false,
message: () => `Expected XML structures to be equal:\n\nReceived:\n${receivedXml}\n\nExpected:\n${expectedXml}`,
pass: false
};
}
},
});
}
};

expect.extend(customMatchers);
41 changes: 41 additions & 0 deletions test/setup/toBePositionedRelativeTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export function toBePositionedRelativeTo(received, other, position) {
if (!(received instanceof Element) || !(other instanceof Element)) {
return {
pass: false,
message: () => `Expected both arguments to be DOM elements.`
};
}

const rectA = received.getBoundingClientRect();
const rectB = other.getBoundingClientRect();

// Check for overlap (Fail if they overlap)
const overlaps =
rectA.left < rectB.right && rectA.right > rectB.left && rectA.top < rectB.bottom && rectA.bottom > rectB.top;

if (overlaps) {
return {
pass: false,
message: () => `Expected elements NOT to overlap, but they do.`
};
}

const positionChecks = {
left: rectA.right <= rectB.left,
right: rectA.left >= rectB.right,
above: rectA.bottom <= rectB.top,
below: rectA.top >= rectB.bottom
};

if (!positionChecks[position]) {
return {
pass: false,
message: () => `Expected element to be "${position}" relative to the other, but it was not.`
};
}

return {
pass: true,
message: () => `Element is correctly positioned "${position}" without overlapping.`
};
}
9 changes: 9 additions & 0 deletions test/storybookMatchers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import '@storybook/test';

declare global {
namespace jest {
interface Matchers<R, T = {}> {
toBePositionedRelativeTo(other: Element, position: 'left' | 'right' | 'above' | 'below'): R;
}
}
}
5 changes: 5 additions & 0 deletions test/vitest.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import 'vitest';

interface CustomMatchers<R = unknown> {
toEqualXml: (expected: string) => R;
toBePositionedRelativeTo: (received, other, position: 'left' | 'right' | 'above' | 'below') => R;
}

interface Assertion<T = any> {
toBePositionedRelativeTo(other: Element, position: 'left' | 'right' | 'above' | 'below'): void;
}

declare module 'vitest' {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,

"types": ["vitest/globals","@types/dom-view-transitions","./test/vitest.d.ts"]
"types": ["vitest/globals","@types/dom-view-transitions","./test/vitest.d.ts", "./test/storybookMatchers.d.ts"]
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
Expand Down

0 comments on commit 5040077

Please sign in to comment.