Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type {i18n} from 'i18next';
import {html} from 'lit';
import {beforeAll, describe, expect, it} from 'vitest';
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
import {createTestI18n} from '@/vitest-utils/testing-helpers/i18n-utils';
import {renderSnippetFooter, type SnippetFooterProps} from './snippet-footer';

describe('#renderSnippetFooter', () => {
let i18n: i18n;

beforeAll(async () => {
i18n = await createTestI18n();
});

const renderComponent = async (
props: Partial<SnippetFooterProps> = {},
children = html``
) => {
const element = await renderFunctionFixture(
html`${renderSnippetFooter({
props: {
i18n,
...props,
},
})(children)}`
);

return element.querySelector('footer') as HTMLElement;
};

it('should render a footer element', async () => {
const footer = await renderComponent({});
expect(footer).toBeInTheDocument();
});

it('should render with footer part', async () => {
const footer = await renderComponent({});
expect(footer.getAttribute('part')).toBe('footer');
});

it('should render with aria-label', async () => {
const footer = await renderComponent({});
expect(footer.getAttribute('aria-label')).toBe(
i18n.t('smart-snippet-source')
);
});

it('should render children inside the footer', async () => {
const children = html`<p>Footer Content</p>`;
const footer = await renderComponent({}, children);
expect(footer?.textContent).toContain('Footer Content');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {i18n} from 'i18next';
import {html} from 'lit';
import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils';

export interface SnippetFooterProps {
i18n: i18n;
}

export const renderSnippetFooter: FunctionalComponentWithChildren<
SnippetFooterProps
> =
({props}) =>
(children) => {
return html`<footer
part="footer"
aria-label=${props.i18n.t('smart-snippet-source')}
>
${children}
</footer>`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {html} from 'lit';
import {describe, expect, it} from 'vitest';
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
import {
renderSnippetQuestion,
type SnippetQuestionProps,
} from './snippet-question';

describe('#renderSnippetQuestion', () => {
const renderComponent = async (props: Partial<SnippetQuestionProps> = {}) => {
const element = await renderFunctionFixture(
html`${renderSnippetQuestion({
props: {
question: 'What is a smart snippet?',
headingLevel: undefined,
...props,
},
})}`
);

return element.firstElementChild as HTMLElement;
};

it('should render the question text', async () => {
const question = await renderComponent({
question: 'What is a smart snippet?',
});
expect(question.textContent?.trim()).toBe('What is a smart snippet?');
});

it('should render with text-xl and font-bold classes', async () => {
const question = await renderComponent({});
expect(question).toHaveClass('text-xl');
expect(question).toHaveClass('font-bold');
});

it('should render with the question part', async () => {
const question = await renderComponent({});
expect(question.getAttribute('part')).toBe('question');
});

it('should render as a div when headingLevel is undefined', async () => {
const question = await renderComponent({headingLevel: undefined});
expect(question.tagName.toLowerCase()).toBe('div');
});

it('should render as a div when headingLevel is 0', async () => {
const question = await renderComponent({headingLevel: 0});
expect(question.tagName.toLowerCase()).toBe('div');
});

it('should render with incremented heading level', async () => {
const question = await renderComponent({headingLevel: 2});
expect(question.tagName.toLowerCase()).toBe('h3');
});

it('should render as h2 when headingLevel is 1', async () => {
const question = await renderComponent({headingLevel: 1});
expect(question.tagName.toLowerCase()).toBe('h2');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {html} from 'lit';
import type {FunctionalComponent} from '@/src/utils/functional-component-utils';
import {renderHeading} from '../../heading';

export interface SnippetQuestionProps {
headingLevel?: number;
question: string;
}

export const renderSnippetQuestion: FunctionalComponent<
SnippetQuestionProps
> = ({props}) => {
return renderHeading({
props: {
level: props.headingLevel ? props.headingLevel + 1 : 0,
class: 'text-xl font-bold',
part: 'question',
},
})(html`${props.question}`);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {html} from 'lit';
import {describe, expect, it} from 'vitest';
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
import {
renderSnippetTruncatedAnswer,
type SnippetTruncatedAnswerProps,
} from './snippet-truncated-answer';

describe('#renderSnippetTruncatedAnswer', () => {
const renderComponent = async (
props: Partial<SnippetTruncatedAnswerProps> = {}
) => {
const element = await renderFunctionFixture(
html`${renderSnippetTruncatedAnswer({
props: {
answer: '<p>This is the answer</p>',
style: undefined,
...props,
},
})}`
);

return element.firstElementChild as HTMLElement;
};

it('should render a div with truncated-answer part', async () => {
const answer = await renderComponent({});
expect(answer).toBeInTheDocument();
expect(answer.getAttribute('part')).toBe('truncated-answer');
});

it('should render atomic-smart-snippet-answer component', async () => {
const answer = await renderComponent({});
const answerElement = answer.querySelector('atomic-smart-snippet-answer');
expect(answerElement).toBeInTheDocument();
});

it('should set htmlContent property on atomic-smart-snippet-answer', async () => {
const answer = await renderComponent({answer: '<p>Test answer</p>'});
const answerElement = answer.querySelector(
'atomic-smart-snippet-answer'
// biome-ignore lint/suspicious/noExplicitAny: testing property access on custom element
) as any;
expect(answerElement.htmlContent).toBe('<p>Test answer</p>');
});

it('should set innerStyle property when style is provided', async () => {
const answer = await renderComponent({style: 'color: red;'});
const answerElement = answer.querySelector(
'atomic-smart-snippet-answer'
// biome-ignore lint/suspicious/noExplicitAny: testing property access on custom element
) as any;
expect(answerElement.innerStyle).toBe('color: red;');
});

it('should set exportparts attribute', async () => {
const answer = await renderComponent({});
const answerElement = answer.querySelector('atomic-smart-snippet-answer');
expect(answerElement?.getAttribute('exportparts')).toBe('answer');
});

it('should set part attribute to body', async () => {
const answer = await renderComponent({});
const answerElement = answer.querySelector('atomic-smart-snippet-answer');
expect(answerElement?.getAttribute('part')).toBe('body');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {html} from 'lit';
import {ifDefined} from 'lit/directives/if-defined.js';
import type {FunctionalComponent} from '@/src/utils/functional-component-utils';

export interface SnippetTruncatedAnswerProps {
answer: string;
style?: string;
}

export const renderSnippetTruncatedAnswer: FunctionalComponent<
SnippetTruncatedAnswerProps
> = ({props}) => {
return html`<div part="truncated-answer">
<atomic-smart-snippet-answer
exportparts="answer"
part="body"
.htmlContent=${props.answer}
.innerStyle=${ifDefined(props.style)}
></atomic-smart-snippet-answer>
</div>`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type {i18n} from 'i18next';
import {html} from 'lit';
import {beforeAll, describe, expect, it} from 'vitest';
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
import {createTestI18n} from '@/vitest-utils/testing-helpers/i18n-utils';
import {
renderSnippetWrapper,
type SnippetWrapperProps,
} from './snippet-wrapper';

describe('#renderSnippetWrapper', () => {
let i18n: i18n;

beforeAll(async () => {
i18n = await createTestI18n();
});

const renderComponent = async (
props: Partial<SnippetWrapperProps> = {},
children = html``
) => {
const element = await renderFunctionFixture(
html`${renderSnippetWrapper({
props: {
i18n,
headingLevel: undefined,
...props,
},
})(children)}`
);

return element.querySelector('aside') as HTMLElement;
};

it('should render an aside element with aria-label', async () => {
const aside = await renderComponent({});
expect(aside).toBeInTheDocument();
expect(aside.getAttribute('aria-label')).toBe(i18n.t('smart-snippet'));
});

it('should render an article element with the smart-snippet part', async () => {
const aside = await renderComponent({});
const article = aside.querySelector('article[part="smart-snippet"]');
expect(article).toBeInTheDocument();
});

it('should render with background and border classes', async () => {
const aside = await renderComponent({});
const article = aside.querySelector('article');
expect(article).toHaveClass('bg-background');
expect(article).toHaveClass('border-neutral');
expect(article).toHaveClass('text-on-background');
expect(article).toHaveClass('rounded-lg');
expect(article).toHaveClass('border');
});

it('should render a heading with sr-only class', async () => {
const aside = await renderComponent({});
const heading = aside.querySelector('.sr-only');
expect(heading).toBeInTheDocument();
expect(heading?.textContent?.trim()).toBe(i18n.t('smart-snippet'));
});

it('should render heading with level 0 when headingLevel is undefined', async () => {
const aside = await renderComponent({headingLevel: undefined});
const heading = aside.querySelector('div.sr-only');
expect(heading).toBeInTheDocument();
});

it('should render heading with specified level', async () => {
const aside = await renderComponent({headingLevel: 2});
const heading = aside.querySelector('h2.sr-only');
expect(heading).toBeInTheDocument();
});

it('should render children inside the article', async () => {
const children = html`<p>Test Content</p>`;
const aside = await renderComponent({}, children);
const article = aside.querySelector('article');
expect(article?.textContent).toContain('Test Content');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type {i18n} from 'i18next';
import {html} from 'lit';
import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils';
import {renderHeading} from '../../heading';

export interface SnippetWrapperProps {
headingLevel?: number;
i18n: i18n;
}

export const renderSnippetWrapper: FunctionalComponentWithChildren<
SnippetWrapperProps
> =
({props}) =>
(children) => {
return html`<aside aria-label=${props.i18n.t('smart-snippet')}>
${renderHeading({
props: {level: props.headingLevel ?? 0, class: 'sr-only'},
})(html`${props.i18n.t('smart-snippet')}`)}
<article
class="bg-background border-neutral text-on-background rounded-lg border p-6 pb-4"
part="smart-snippet"
>
${children}
</article>
</aside>`;
};
Loading
Loading