Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ export const InstrumentSelector = () => {
{instruments.map((instrument) => (
<Command.Item
key={instrument.id}
value={instrument.id}
onSelect={(id) => {
setSelectedInstrument(id);
value={instrument.label}
onSelect={() => {
setSelectedInstrument(instrument.id);
setOpen(false);
}}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// @vitest-environment happy-dom

import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import type { InstrumentRepository } from '@/models/instrument-repository.model';

import { InstrumentSelector } from '../InstrumentSelector';

const store = vi.hoisted(() => {
const instruments: InstrumentRepository[] = [
{
category: 'Examples',
files: [],
id: '0b5d1c2e-7f3a-4c1d-9e8f-2a6b7c8d9e0f',
kind: 'FORM',
label: 'Form Reference'
},
{
category: 'Templates',
files: [],
id: 'c3e9a1b2-5d6f-4e7a-8b9c-0d1e2f3a4b5c',
kind: 'FORM',
label: 'Unilingual Form'
}
];
return {
state: {
instruments,
selectedInstrument: instruments[1]!,
setSelectedInstrument: (_id: string) => undefined
}
};
});

vi.mock('@/store', () => ({
useAppStore: <TSelected,>(selector: (state: typeof store.state) => TSelected) => selector(store.state)
}));

function openAndSearch(query: string) {
fireEvent.click(screen.getByRole('combobox', { name: 'Load an instrument...' }));
fireEvent.change(screen.getByPlaceholderText('Search...'), { target: { value: query } });
}

describe('InstrumentSelector', () => {
beforeEach(() => {
store.state.setSelectedInstrument = vi.fn();
render(<InstrumentSelector />);
});

afterEach(() => {
cleanup();
});

it('should list an instrument whose label matches the search', () => {
openAndSearch('Form Reference');
expect(screen.getByRole('option', { name: 'Form Reference' })).toBeDefined();
expect(screen.queryByRole('option', { name: 'Unilingual Form' })).toBeNull();
});

it('should list nothing for a query only an id matches, so the hidden UUIDs are never searched', () => {
openAndSearch('4');
expect(screen.queryAllByRole('option')).toHaveLength(0);
expect(screen.getByText('No Instruments Found')).toBeDefined();
});

it('should select the chosen instrument by its id, though its label is what is searched', () => {
openAndSearch('Form Reference');
fireEvent.click(screen.getByRole('option', { name: 'Form Reference' }));
expect(store.state.setSelectedInstrument).toHaveBeenCalledExactlyOnceWith('0b5d1c2e-7f3a-4c1d-9e8f-2a6b7c8d9e0f');
});
});
26 changes: 20 additions & 6 deletions testing/src/pages/playground/index.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { playgroundURL } from '../../support/env';
export class PlaygroundPage {
readonly $ref: Page;
readonly editorPane: Locator;
/** The instrument list's search box, inside the popover {@link instrumentSelector} opens. */
readonly instrumentSearch: Locator;
/** The header's button that opens the instrument list. */
readonly instrumentSelector: Locator;
/** The handle between the editor and preview panes, which resizes them. */
readonly paneSeparator: Locator;
/** The instrument preview, which runs on another origin than the editor. */
Expand All @@ -19,6 +23,8 @@ export class PlaygroundPage {
constructor(page: Page) {
this.$ref = page;
this.editorPane = page.getByTestId('editor-pane');
this.instrumentSelector = page.getByRole('combobox', { name: 'Load an instrument...' });
this.instrumentSearch = page.getByPlaceholder('Search...');
this.paneSeparator = page.getByRole('separator');
this.previewFrame = page.getByTestId('preview-frame');
this.preview = page.frameLocator('[data-testid="preview-frame"]');
Expand Down Expand Up @@ -46,12 +52,20 @@ export class PlaygroundPage {
await this.$ref.goto(url);
}

/**
* Picks an example or template from the header's instrument list by its label. The list's search
* box matches on instrument ids rather than labels, so the option is clicked without filtering.
*/
/** The option for the instrument with this label in the header's instrument list. */
instrumentOption(label: string): Locator {
return this.$ref.getByRole('option', { exact: true, name: label });
}

/** Opens the header's instrument list and types `query` into its search box. */
async searchInstruments(query: string): Promise<void> {
await this.instrumentSelector.click();
await this.instrumentSearch.fill(query);
}

/** Picks an example or template from the header's instrument list by its label. */
async selectInstrument(label: string): Promise<void> {
await this.$ref.getByRole('combobox', { name: 'Load an instrument...' }).click();
await this.$ref.getByRole('option', { exact: true, name: label }).click();
await this.searchInstruments(label);
await this.instrumentOption(label).click();
}
}
17 changes: 17 additions & 0 deletions testing/src/specs/playground.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ test.describe('playground', () => {
test.describe.configure({ mode: 'default' });
test.slow();

test('should find an instrument by its visible label in the selector', async ({ page }) => {
const playground = new PlaygroundPage(page);
await playground.goto();

await playground.searchInstruments('Form Reference');
await expect(playground.instrumentOption('Form Reference')).toBeVisible();
});

test('should find nothing for a query only the hidden instrument ids match', async ({ page }) => {
const playground = new PlaygroundPage(page);
await playground.goto();

// Every v4 UUID carries a 4 in its version digit, and no built-in label contains one.
await playground.searchInstruments('4');
await expect(page.getByText('No Instruments Found')).toBeVisible();
});

test('should run the preview on an origin other than the editor and render the instrument there @smoke', async ({
page
}) => {
Expand Down
Loading