@testing-library/react#queryByText TypeScript Examples
The following examples show how to use
@testing-library/react#queryByText.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should mutate feed sorting enabled setting', () =>
testSettingsMutation({ sortingEnabled: true }, async () => {
const checkboxes = await screen.findAllByRole('checkbox');
const checkbox = checkboxes.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Show feed sorting menu'),
) as HTMLInputElement;
fireEvent.click(checkbox);
}));
Example #2
Source File: UMLEditor.test.tsx From legend-studio with Apache License 2.0 | 6 votes |
test(integrationTest('Enumeration editor'), async () => {
await TEST__openElementFromExplorerTree('ui::TestEnumeration', renderResult);
const editPanelHeader = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.EDIT_PANEL__HEADER_TABS,
);
expect(getByText(editPanelHeader, 'TestEnumeration')).not.toBeNull();
const enumerationEditor = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.ENUMERATION_EDITOR,
);
const enums = ['enumA', 'enumB', 'enumC'];
enums.forEach((e) => getByDisplayValue(enumerationEditor, e));
fireEvent.click(getByText(enumerationEditor, 'Tagged Values'));
await waitFor(() => getByText(enumerationEditor, 'ProfileTest'));
getByDisplayValue(enumerationEditor, 'Enumeration Tag');
fireEvent.click(getByText(enumerationEditor, 'Stereotypes'));
await waitFor(() => getByText(enumerationEditor, 'stereotype2'));
fireEvent.click(getByText(enumerationEditor, 'Values'));
await waitFor(() => getByDisplayValue(enumerationEditor, 'enumA'));
const enumB = getByDisplayValue(enumerationEditor, 'enumA');
const parentElement = enumB.parentElement?.parentElement as HTMLElement;
const buttons = queryAllByRole(parentElement, 'button');
expect(buttons).toHaveLength(2);
fireEvent.click(guaranteeNonNullable(buttons[0])); // navigate
await waitFor(() => getByText(enumerationEditor, 'enum'));
const subPropertyPanel = getByTestId(
enumerationEditor,
LEGEND_STUDIO_TEST_ID.PANEL,
);
getByDisplayValue(subPropertyPanel, 'enumATag');
fireEvent.click(getByText(subPropertyPanel, 'Stereotypes'));
await waitFor(() => getByText(subPropertyPanel, 'stereotype1'));
fireEvent.click(
guaranteeNonNullable(queryAllByRole(subPropertyPanel, 'button')[0]),
);
fireEvent.click(guaranteeNonNullable(buttons[1])); // delete
expect(queryByText(enumerationEditor, 'enumA')).toBeNull();
});
Example #3
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should mutate Show custom shortcuts setting in extension', async () => {
process.env.TARGET_BROWSER = 'chrome';
renderComponent();
let mutationCalled = false;
mockGraphQL({
request: {
query: UPDATE_USER_SETTINGS_MUTATION,
variables: { data: { ...defaultSettings, showTopSites: false } },
},
result: () => {
mutationCalled = true;
return { data: { updateUserSettings: { updatedAt: new Date(0) } } };
},
});
const checkboxes = await screen.findAllByRole('checkbox');
const checkbox = checkboxes.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Show custom shortcuts'),
) as HTMLInputElement;
await waitFor(() => expect(checkbox).toBeInTheDocument());
await waitFor(() => expect(checkbox).toBeChecked());
fireEvent.click(checkbox);
await waitFor(() => expect(mutationCalled).toBeTruthy());
});
Example #4
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should mutate show weekly goals widget setting', () =>
testSettingsMutation({ optOutWeeklyGoal: false }, async () => {
const checkboxes = await screen.findAllByRole('checkbox');
const checkbox = checkboxes.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Show Weekly Goal widget'),
) as HTMLInputElement;
fireEvent.click(checkbox);
}));
Example #5
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should mutate open links in new tab setting', () =>
testSettingsMutation({ openNewTab: true }, async () => {
const checkboxes = await screen.findAllByRole('checkbox');
const checkbox = checkboxes.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Open links in new tab'),
) as HTMLInputElement;
fireEvent.click(checkbox);
}));
Example #6
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should mutate hide read posts setting', () =>
testSettingsMutation({ showOnlyUnreadPosts: false }, async () => {
const checkboxes = await screen.findAllByRole('checkbox');
const checkbox = checkboxes.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Hide read posts'),
) as HTMLInputElement;
fireEvent.click(checkbox);
}));
Example #7
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should set light to dark mode setting', () =>
testSettingsMutation(
{ theme: 'bright' },
async () => {
const radios = await screen.findAllByRole('radio');
const radio = radios.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Light'),
) as HTMLInputElement;
fireEvent.click(radio);
},
{ ...defaultSettings, theme: 'darcula' },
));
Example #8
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should set theme to dark mode setting', () =>
testSettingsMutation({ theme: 'darcula' }, async () => {
const radios = await screen.findAllByRole('radio');
const radio = radios.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Dark'),
) as HTMLInputElement;
fireEvent.click(radio);
}));
Example #9
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should utilize local cache settings for anonymous users', async () => {
const localBootData = {
...defaultBootData,
settings: { ...defaultBootData.settings, theme: 'cozy' },
};
localStorage.setItem(BOOT_LOCAL_KEY, JSON.stringify(localBootData));
renderBootProvider(defaultBootData);
const radio = await screen.findAllByRole('radio');
await waitFor(() =>
expect(
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
radio.find((el) => queryByText(el.parentElement, 'Cozy')),
).toBeChecked(),
);
});
Example #10
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
it('should utilize front-end default settings for first time users', async () => {
renderBootProvider();
const radio = await screen.findAllByRole('radio');
await waitFor(() =>
expect(
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
radio.find((el) => queryByText(el.parentElement, 'Eco')),
).toBeChecked(),
);
});
Example #11
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 6 votes |
testSettingsMutation = async (
settings: Partial<RemoteSettings>,
updateFunc: () => Promise<void>,
initialSettings = defaultSettings,
): Promise<void> => {
renderComponent(defaultUser, initialSettings);
let mutationCalled = false;
mockGraphQL({
request: {
query: UPDATE_USER_SETTINGS_MUTATION,
variables: { data: { ...defaultSettings, ...settings } },
},
result: () => {
mutationCalled = true;
return { data: { updateUserSettings: { updatedAt: new Date(0) } } };
},
});
if (initialSettings.theme === 'bright') {
const radio = await screen.findAllByRole('radio');
await waitFor(() =>
expect(
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
radio.find((el) => queryByText(el.parentElement, 'Light')),
).toBeChecked(),
);
}
await updateFunc();
await waitFor(() => expect(mutationCalled).toBeTruthy());
}
Example #12
Source File: AsyncDualSelectWidget.test.tsx From ke with MIT License | 6 votes |
test('It should move the item from the left list to the right list', async () => {
const screen = render(<AsyncDualSelectWidget {...(defaultProps as any)} />)
await waitFor(() => screen.findByText('test'))
const leftList = screen.queryByTestId('ds-left-list')
await waitFor(() => findByText(leftList as HTMLElement, 'test'))
expect(leftList?.textContent).toContain('test')
const testOption = screen.getByText('test')
const selectButton = screen.getByText('SELECT')
fireEvent.click(testOption)
fireEvent.click(selectButton)
const rightList = screen.queryByTestId('ds-right-list')
expect(getByText(rightList as HTMLElement, 'test')).not.toBeNull()
expect(queryByText(leftList as HTMLElement, 'test')).toBeNull()
})
Example #13
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
it('should mutate density setting', () =>
testSettingsMutation({ spaciness: 'cozy' }, async () => {
const radio = await screen.findAllByRole('radio');
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
const cozy = radio.find((el) => queryByText(el.parentElement, 'Cozy'));
fireEvent.click(cozy);
}));
Example #14
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
it('should fetch remote settings', async () => {
renderComponent();
const radio = await screen.findAllByRole('radio');
await waitFor(() =>
expect(
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
radio.find((el) => queryByText(el.parentElement, 'Roomy')),
).toBeChecked(),
);
await waitFor(() =>
expect(
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
radio.find((el) => queryByText(el.parentElement, 'Light')),
).toBeChecked(),
);
const checkbox = await screen.findAllByRole('checkbox');
await waitFor(() =>
expect(
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
checkbox.find((el) => queryByText(el.parentElement, 'Hide read posts')),
).toBeChecked(),
);
await waitFor(() =>
expect(
checkbox.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Open links in new tab'),
),
).not.toBeChecked(),
);
await waitFor(() =>
expect(
checkbox.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Show feed sorting menu'),
),
).not.toBeChecked(),
);
});
Example #15
Source File: Settings.spec.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
it('should not have the Show custom shortcuts switch in the webapp', async () => {
renderComponent(null);
const checkbox = screen.queryByText('Show custom shortcuts');
expect(checkbox).not.toBeInTheDocument();
});
Example #16
Source File: SharedBookmarksModal.spec.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
it('should enable public mode on toggle click', async () => {
let mutationCalled = false;
renderComponent([
{
request: {
query: BOOKMARK_SHARING_QUERY,
},
result: {
data: {
bookmarksSharing: {
enabled: false,
slug: '',
rssUrl: '',
},
},
},
},
{
request: {
query: BOOKMARK_SHARING_MUTATION,
variables: { enabled: true },
},
result: () => {
mutationCalled = true;
return {
data: {
bookmarksSharing: {
enabled: true,
slug: '619f6044-c02b-486b-8234-9a46ad1bb604',
rssUrl:
'http://localhost:4000/rss/b/619f6044-c02b-486b-8234-9a46ad1bb604',
},
},
};
},
},
]);
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
const checkboxes = await screen.findAllByRole('checkbox');
const checkbox = checkboxes.find((el) =>
// eslint-disable-next-line testing-library/no-node-access, testing-library/prefer-screen-queries
queryByText(el.parentElement, 'Public mode'),
) as HTMLInputElement;
fireEvent.click(checkbox);
await waitFor(() => expect(mutationCalled).toBeTruthy());
const input = screen.getByDisplayValue(
'http://localhost:4000/rss/b/619f6044-c02b-486b-8234-9a46ad1bb604',
);
await waitFor(() => expect(input).toBeInTheDocument());
});
Example #17
Source File: EditPanel.test.tsx From legend-studio with Apache License 2.0 | 5 votes |
test(integrationTest('Test navigation between element states'), async () => {
// Test opening multiple elements
await TEST__openElementFromExplorerTree('ui::test1::Animal', renderResult);
const packageExplorer = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.EXPLORER_TREES,
);
fireEvent.click(getByText(packageExplorer, 'TestClass'));
fireEvent.click(getByText(packageExplorer, 'TestEnumeration'));
fireEvent.click(getByText(packageExplorer, 'Anyone'));
fireEvent.click(getByText(packageExplorer, 'Dog'));
fireEvent.click(getByText(packageExplorer, 'Something'));
fireEvent.click(getByText(packageExplorer, 'ProfileTest'));
const editPanelHeader = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.EDIT_PANEL__HEADER_TABS,
);
await waitFor(() => getByText(editPanelHeader, 'ProfileTest'));
const openElements = [
'TestEnumeration',
'TestClass',
'Anyone',
'Dog',
'Something',
'ProfileTest',
];
openElements.forEach((openEl) => getByText(editPanelHeader, openEl));
// navigate through visit buttons
fireEvent.click(getByText(editPanelHeader, 'TestClass'));
await waitFor(() => renderResult.getByText('founder'));
const navigateToClass = async (className: string): Promise<void> => {
const classForm = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.CLASS_FORM_EDITOR,
);
const property = await waitFor(() => getByText(classForm, className));
const propertyBasicEditor = property.parentElement as HTMLElement;
const navigateButton = getByTestId(
propertyBasicEditor,
LEGEND_STUDIO_TEST_ID.TYPE_VISIT,
);
fireEvent.click(navigateButton);
await waitFor(() => getByText(editPanelHeader, className));
};
await navigateToClass('Person');
await navigateToClass('Firm');
await navigateToClass('Person');
await navigateToClass('Degree');
const newOpened = ['Firm', 'Degree', 'Person'];
openElements
.concat(newOpened)
.forEach((openElement) => getByText(editPanelHeader, openElement));
// test closing of tabs
const closeTabs = ['Firm', 'Degree', 'TestEnumeration'];
closeTabs.forEach((tab) => {
const text = getByText(editPanelHeader, tab);
const parent = text.parentElement as HTMLElement;
const deleteButton = getByTitle(parent, 'Close');
fireEvent.click(deleteButton);
});
closeTabs.forEach((tab) =>
expect(queryByText(editPanelHeader, tab)).toBeNull(),
);
// TODO Add Diff Element States
});
Example #18
Source File: OwnershipCard.test.tsx From backstage with Apache License 2.0 | 4 votes |
describe('OwnershipCard', () => {
const groupEntity: GroupEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'my-team',
},
spec: {
type: 'team',
children: [],
},
relations: [
{
type: 'memberOf',
targetRef: 'group:default/examplegroup',
},
],
};
it('displays entity counts', async () => {
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={groupEntity}>
<OwnershipCard />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(catalogApi.getEntities).toHaveBeenCalledWith({
filter: [
{
kind: ['Component', 'API', 'System'],
'relations.ownedBy': ['group:default/my-team'],
},
],
fields: [
'kind',
'metadata.name',
'metadata.namespace',
'spec.type',
'relations',
],
});
expect(getByText('OPENAPI')).toBeInTheDocument();
expect(
queryByText(getByText('OPENAPI').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('SERVICE')).toBeInTheDocument();
expect(
queryByText(getByText('SERVICE').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('LIBRARY')).toBeInTheDocument();
expect(
queryByText(getByText('LIBRARY').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('SYSTEM')).toBeInTheDocument();
expect(
queryByText(getByText('SYSTEM').parentElement!, '1'),
).toBeInTheDocument();
});
it('applies CustomFilterDefinition', async () => {
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={groupEntity}>
<OwnershipCard entityFilterKind={['API', 'System']} />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(getByText('SYSTEM')).toBeInTheDocument();
expect(
queryByText(getByText('SYSTEM').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('OPENAPI')).toBeInTheDocument();
expect(
queryByText(getByText('OPENAPI').parentElement!, '1'),
).toBeInTheDocument();
expect(() => getByText('LIBRARY')).toThrowError();
});
it('links to the catalog with the group filter', async () => {
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={groupEntity}>
<OwnershipCard />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(getByText('OPENAPI').closest('a')).toHaveAttribute(
'href',
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D=my-team&filters%5Buser%5D=all',
);
});
it('links to the catalog with the user and groups filters from an user profile', async () => {
const userEntity: UserEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'the-user',
},
spec: {
memberOf: ['my-team'],
},
relations: [
{
type: 'memberOf',
targetRef: 'group:default/my-team',
},
{
type: 'memberOf',
targetRef: 'group:custom/some-team',
},
],
};
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={userEntity}>
<OwnershipCard />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(getByText('OPENAPI').closest('a')).toHaveAttribute(
'href',
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D=user%3Athe-user&filters%5Bowners%5D=my-team&filters%5Bowners%5D=custom%2Fsome-team&filters%5Buser%5D=all',
);
});
describe('OwnershipCard relations', () => {
it('shows relations toggle', async () => {
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByTitle } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={groupEntity}>
<OwnershipCard />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(getByTitle('Direct Relations')).toBeInTheDocument();
});
it('hides relations toggle', async () => {
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const rendered = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={groupEntity}>
<OwnershipCard hideRelationsToggle />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(rendered.queryByText('Direct Relations')).toBeNull();
});
it('overrides relation type', async () => {
const catalogApi: jest.Mocked<CatalogApi> = {
getEntities: jest.fn(),
} as any;
catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByTitle } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<EntityProvider entity={groupEntity}>
<OwnershipCard relationsType="aggregated" />
</EntityProvider>
</TestApiProvider>,
{
mountedRoutes: {
'/create': catalogIndexRouteRef,
},
},
);
expect(getByTitle('Aggregated Relations')).toBeInTheDocument();
});
});
});
Example #19
Source File: textWysiwyg.test.tsx From excalidraw with MIT License | 4 votes |
describe("textWysiwyg", () => {
describe("start text editing", () => {
const { h } = window;
beforeEach(async () => {
await render(<ExcalidrawApp />);
h.elements = [];
});
it("should prefer editing selected text element (non-bindable container present)", async () => {
const line = API.createElement({
type: "line",
width: 100,
height: 0,
points: [
[0, 0],
[100, 0],
],
});
const textSize = 20;
const text = API.createElement({
type: "text",
text: "ola",
x: line.width / 2 - textSize / 2,
y: -textSize / 2,
width: textSize,
height: textSize,
});
h.elements = [text, line];
API.setSelectedElements([text]);
Keyboard.keyPress(KEYS.ENTER);
expect(h.state.editingElement?.id).toBe(text.id);
expect(
(h.state.editingElement as ExcalidrawTextElement).containerId,
).toBe(null);
});
it("should prefer editing selected text element (bindable container present)", async () => {
const container = API.createElement({
type: "rectangle",
width: 100,
boundElements: [],
});
const textSize = 20;
const boundText = API.createElement({
type: "text",
text: "ola",
x: container.width / 2 - textSize / 2,
y: container.height / 2 - textSize / 2,
width: textSize,
height: textSize,
containerId: container.id,
});
const boundText2 = API.createElement({
type: "text",
text: "ola",
x: container.width / 2 - textSize / 2,
y: container.height / 2 - textSize / 2,
width: textSize,
height: textSize,
containerId: container.id,
});
h.elements = [container, boundText, boundText2];
mutateElement(container, {
boundElements: [{ type: "text", id: boundText.id }],
});
API.setSelectedElements([boundText2]);
Keyboard.keyPress(KEYS.ENTER);
expect(h.state.editingElement?.id).toBe(boundText2.id);
});
it("should not create bound text on ENTER if text exists at container center", () => {
const container = API.createElement({
type: "rectangle",
width: 100,
});
const textSize = 20;
const text = API.createElement({
type: "text",
text: "ola",
x: container.width / 2 - textSize / 2,
y: container.height / 2 - textSize / 2,
width: textSize,
height: textSize,
containerId: container.id,
});
h.elements = [container, text];
API.setSelectedElements([container]);
Keyboard.keyPress(KEYS.ENTER);
expect(h.state.editingElement?.id).toBe(text.id);
});
it("should edit existing bound text on ENTER even if higher z-index unbound text exists at container center", () => {
const container = API.createElement({
type: "rectangle",
width: 100,
boundElements: [],
});
const textSize = 20;
const boundText = API.createElement({
type: "text",
text: "ola",
x: container.width / 2 - textSize / 2,
y: container.height / 2 - textSize / 2,
width: textSize,
height: textSize,
containerId: container.id,
});
const boundText2 = API.createElement({
type: "text",
text: "ola",
x: container.width / 2 - textSize / 2,
y: container.height / 2 - textSize / 2,
width: textSize,
height: textSize,
containerId: container.id,
});
h.elements = [container, boundText, boundText2];
mutateElement(container, {
boundElements: [{ type: "text", id: boundText.id }],
});
API.setSelectedElements([container]);
Keyboard.keyPress(KEYS.ENTER);
expect(h.state.editingElement?.id).toBe(boundText.id);
});
it("should edit text under cursor when clicked with text tool", () => {
const text = API.createElement({
type: "text",
text: "ola",
x: 60,
y: 0,
width: 100,
height: 100,
});
h.elements = [text];
UI.clickTool("text");
mouse.clickAt(text.x + 50, text.y + 50);
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
expect(editor).not.toBe(null);
expect(h.state.editingElement?.id).toBe(text.id);
expect(h.elements.length).toBe(1);
});
it("should edit text under cursor when double-clicked with selection tool", () => {
const text = API.createElement({
type: "text",
text: "ola",
x: 60,
y: 0,
width: 100,
height: 100,
});
h.elements = [text];
UI.clickTool("selection");
mouse.doubleClickAt(text.x + 50, text.y + 50);
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
expect(editor).not.toBe(null);
expect(h.state.editingElement?.id).toBe(text.id);
expect(h.elements.length).toBe(1);
});
});
describe("Test container-unbound text", () => {
const { h } = window;
let textarea: HTMLTextAreaElement;
let textElement: ExcalidrawTextElement;
beforeEach(async () => {
await render(<ExcalidrawApp />);
textElement = UI.createElement("text");
mouse.clickOn(textElement);
textarea = document.querySelector(
".excalidraw-textEditorContainer > textarea",
)!;
});
it("should add a tab at the start of the first line", () => {
const event = new KeyboardEvent("keydown", { key: KEYS.TAB });
textarea.value = "Line#1\nLine#2";
// cursor: "|Line#1\nLine#2"
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`${tab}Line#1\nLine#2`);
// cursor: " |Line#1\nLine#2"
expect(textarea.selectionStart).toEqual(4);
expect(textarea.selectionEnd).toEqual(4);
});
it("should add a tab at the start of the second line", () => {
const event = new KeyboardEvent("keydown", { key: KEYS.TAB });
textarea.value = "Line#1\nLine#2";
// cursor: "Line#1\nLin|e#2"
textarea.selectionStart = 10;
textarea.selectionEnd = 10;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`Line#1\n${tab}Line#2`);
// cursor: "Line#1\n Lin|e#2"
expect(textarea.selectionStart).toEqual(14);
expect(textarea.selectionEnd).toEqual(14);
});
it("should add a tab at the start of the first and second line", () => {
const event = new KeyboardEvent("keydown", { key: KEYS.TAB });
textarea.value = "Line#1\nLine#2\nLine#3";
// cursor: "Li|ne#1\nLi|ne#2\nLine#3"
textarea.selectionStart = 2;
textarea.selectionEnd = 9;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`${tab}Line#1\n${tab}Line#2\nLine#3`);
// cursor: " Li|ne#1\n Li|ne#2\nLine#3"
expect(textarea.selectionStart).toEqual(6);
expect(textarea.selectionEnd).toEqual(17);
});
it("should remove a tab at the start of the first line", () => {
const event = new KeyboardEvent("keydown", {
key: KEYS.TAB,
shiftKey: true,
});
textarea.value = `${tab}Line#1\nLine#2`;
// cursor: "| Line#1\nLine#2"
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`Line#1\nLine#2`);
// cursor: "|Line#1\nLine#2"
expect(textarea.selectionStart).toEqual(0);
expect(textarea.selectionEnd).toEqual(0);
});
it("should remove a tab at the start of the second line", () => {
const event = new KeyboardEvent("keydown", {
key: KEYS.TAB,
shiftKey: true,
});
// cursor: "Line#1\n Lin|e#2"
textarea.value = `Line#1\n${tab}Line#2`;
textarea.selectionStart = 15;
textarea.selectionEnd = 15;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`Line#1\nLine#2`);
// cursor: "Line#1\nLin|e#2"
expect(textarea.selectionStart).toEqual(11);
expect(textarea.selectionEnd).toEqual(11);
});
it("should remove a tab at the start of the first and second line", () => {
const event = new KeyboardEvent("keydown", {
key: KEYS.TAB,
shiftKey: true,
});
// cursor: " Li|ne#1\n Li|ne#2\nLine#3"
textarea.value = `${tab}Line#1\n${tab}Line#2\nLine#3`;
textarea.selectionStart = 6;
textarea.selectionEnd = 17;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`Line#1\nLine#2\nLine#3`);
// cursor: "Li|ne#1\nLi|ne#2\nLine#3"
expect(textarea.selectionStart).toEqual(2);
expect(textarea.selectionEnd).toEqual(9);
});
it("should remove a tab at the start of the second line and cursor stay on this line", () => {
const event = new KeyboardEvent("keydown", {
key: KEYS.TAB,
shiftKey: true,
});
// cursor: "Line#1\n | Line#2"
textarea.value = `Line#1\n${tab}Line#2`;
textarea.selectionStart = 9;
textarea.selectionEnd = 9;
textarea.dispatchEvent(event);
// cursor: "Line#1\n|Line#2"
expect(textarea.selectionStart).toEqual(7);
// expect(textarea.selectionEnd).toEqual(7);
});
it("should remove partial tabs", () => {
const event = new KeyboardEvent("keydown", {
key: KEYS.TAB,
shiftKey: true,
});
// cursor: "Line#1\n Line#|2"
textarea.value = `Line#1\n Line#2`;
textarea.selectionStart = 15;
textarea.selectionEnd = 15;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`Line#1\nLine#2`);
});
it("should remove nothing", () => {
const event = new KeyboardEvent("keydown", {
key: KEYS.TAB,
shiftKey: true,
});
// cursor: "Line#1\n Li|ne#2"
textarea.value = `Line#1\nLine#2`;
textarea.selectionStart = 9;
textarea.selectionEnd = 9;
textarea.dispatchEvent(event);
expect(textarea.value).toEqual(`Line#1\nLine#2`);
});
it("should resize text via shortcuts while in wysiwyg", () => {
textarea.value = "abc def";
const origFontSize = textElement.fontSize;
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
key: KEYS.CHEVRON_RIGHT,
ctrlKey: true,
shiftKey: true,
}),
);
expect(textElement.fontSize).toBe(origFontSize * 1.1);
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
key: KEYS.CHEVRON_LEFT,
ctrlKey: true,
shiftKey: true,
}),
);
expect(textElement.fontSize).toBe(origFontSize);
});
it("zooming via keyboard should zoom canvas", () => {
expect(h.state.zoom.value).toBe(1);
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
code: CODES.MINUS,
ctrlKey: true,
}),
);
expect(h.state.zoom.value).toBe(0.9);
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
code: CODES.NUM_SUBTRACT,
ctrlKey: true,
}),
);
expect(h.state.zoom.value).toBe(0.8);
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
code: CODES.NUM_ADD,
ctrlKey: true,
}),
);
expect(h.state.zoom.value).toBe(0.9);
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
code: CODES.EQUAL,
ctrlKey: true,
}),
);
expect(h.state.zoom.value).toBe(1);
});
});
describe("Test container-bound text", () => {
let rectangle: any;
const { h } = window;
const DUMMY_HEIGHT = 240;
const DUMMY_WIDTH = 160;
const APPROX_LINE_HEIGHT = 25;
const INITIAL_WIDTH = 10;
beforeAll(() => {
jest
.spyOn(textElementUtils, "getApproxLineHeight")
.mockReturnValue(APPROX_LINE_HEIGHT);
});
beforeEach(async () => {
await render(<ExcalidrawApp />);
h.elements = [];
rectangle = UI.createElement("rectangle", {
x: 10,
y: 20,
width: 90,
height: 75,
});
});
it("should bind text to container when double clicked on center", async () => {
expect(h.elements.length).toBe(1);
expect(h.elements[0].id).toBe(rectangle.id);
mouse.doubleClickAt(
rectangle.x + rectangle.width / 2,
rectangle.y + rectangle.height / 2,
);
expect(h.elements.length).toBe(2);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.type).toBe("text");
expect(text.containerId).toBe(rectangle.id);
mouse.down();
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
fireEvent.change(editor, { target: { value: "Hello World!" } });
await new Promise((r) => setTimeout(r, 0));
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
});
it("should bind text to container when clicked on container and enter pressed", async () => {
expect(h.elements.length).toBe(1);
expect(h.elements[0].id).toBe(rectangle.id);
Keyboard.withModifierKeys({}, () => {
Keyboard.keyPress(KEYS.ENTER);
});
expect(h.elements.length).toBe(2);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.type).toBe("text");
expect(text.containerId).toBe(rectangle.id);
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
await new Promise((r) => setTimeout(r, 0));
fireEvent.change(editor, { target: { value: "Hello World!" } });
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
});
it("shouldn't bind to non-text-bindable containers", async () => {
const line = API.createElement({
type: "line",
width: 100,
height: 0,
points: [
[0, 0],
[100, 0],
],
});
h.elements = [line];
UI.clickTool("text");
mouse.clickAt(line.x + line.width / 2, line.y + line.height / 2);
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
fireEvent.change(editor, {
target: {
value: "Hello World!",
},
});
fireEvent.keyDown(editor, { key: KEYS.ESCAPE });
editor.dispatchEvent(new Event("input"));
expect(line.boundElements).toBe(null);
expect(h.elements[1].type).toBe("text");
expect((h.elements[1] as ExcalidrawTextElement).containerId).toBe(null);
});
it("should'nt bind text to container when not double clicked on center", async () => {
expect(h.elements.length).toBe(1);
expect(h.elements[0].id).toBe(rectangle.id);
// clicking somewhere on top left
mouse.doubleClickAt(rectangle.x + 20, rectangle.y + 20);
expect(h.elements.length).toBe(2);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.type).toBe("text");
expect(text.containerId).toBe(null);
mouse.down();
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
fireEvent.change(editor, { target: { value: "Hello World!" } });
await new Promise((r) => setTimeout(r, 0));
editor.blur();
expect(rectangle.boundElements).toBe(null);
});
it("should update font family correctly on undo/redo by selecting bounded text when font family was updated", async () => {
expect(h.elements.length).toBe(1);
mouse.doubleClickAt(
rectangle.x + rectangle.width / 2,
rectangle.y + rectangle.height / 2,
);
mouse.down();
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
let editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
await new Promise((r) => setTimeout(r, 0));
fireEvent.change(editor, { target: { value: "Hello World!" } });
editor.blur();
expect(text.fontFamily).toEqual(FONT_FAMILY.Virgil);
UI.clickTool("text");
mouse.clickAt(
rectangle.x + rectangle.width / 2,
rectangle.y + rectangle.height / 2,
);
mouse.down();
editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
editor.select();
fireEvent.click(screen.getByTitle(/code/i));
await new Promise((r) => setTimeout(r, 0));
editor.blur();
expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Cascadia);
//undo
Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress(KEYS.Z);
});
expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Virgil);
//redo
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
Keyboard.keyPress(KEYS.Z);
});
expect(
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
).toEqual(FONT_FAMILY.Cascadia);
});
it("should wrap text and vertcially center align once text submitted", async () => {
jest
.spyOn(textElementUtils, "measureText")
.mockImplementation((text, font, maxWidth) => {
let width = INITIAL_WIDTH;
let height = APPROX_LINE_HEIGHT;
let baseline = 10;
if (!text) {
return {
width,
height,
baseline,
};
}
baseline = 30;
width = DUMMY_WIDTH;
if (text === "Hello \nWorld!") {
height = APPROX_LINE_HEIGHT * 2;
}
if (maxWidth) {
width = maxWidth;
// To capture cases where maxWidth passed is initial width
// due to which the text is not wrapped correctly
if (maxWidth === INITIAL_WIDTH) {
height = DUMMY_HEIGHT;
}
}
return {
width,
height,
baseline,
};
});
expect(h.elements.length).toBe(1);
Keyboard.keyDown(KEYS.ENTER);
let text = h.elements[1] as ExcalidrawTextElementWithContainer;
let editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
// mock scroll height
jest
.spyOn(editor, "scrollHeight", "get")
.mockImplementation(() => APPROX_LINE_HEIGHT * 2);
fireEvent.change(editor, {
target: {
value: "Hello World!",
},
});
editor.dispatchEvent(new Event("input"));
await new Promise((cb) => setTimeout(cb, 0));
editor.blur();
text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.text).toBe("Hello \nWorld!");
expect(text.originalText).toBe("Hello World!");
expect(text.y).toBe(
rectangle.y + rectangle.height / 2 - (APPROX_LINE_HEIGHT * 2) / 2,
);
expect(text.x).toBe(rectangle.x + BOUND_TEXT_PADDING);
expect(text.height).toBe(APPROX_LINE_HEIGHT * 2);
expect(text.width).toBe(rectangle.width - BOUND_TEXT_PADDING * 2);
// Edit and text by removing second line and it should
// still vertically align correctly
mouse.select(rectangle);
Keyboard.withModifierKeys({}, () => {
Keyboard.keyPress(KEYS.ENTER);
});
editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
fireEvent.change(editor, {
target: {
value: "Hello",
},
});
// mock scroll height
jest
.spyOn(editor, "scrollHeight", "get")
.mockImplementation(() => APPROX_LINE_HEIGHT);
editor.style.height = "25px";
editor.dispatchEvent(new Event("input"));
await new Promise((r) => setTimeout(r, 0));
editor.blur();
text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.text).toBe("Hello");
expect(text.originalText).toBe("Hello");
expect(text.y).toBe(
rectangle.y + rectangle.height / 2 - APPROX_LINE_HEIGHT / 2,
);
expect(text.x).toBe(rectangle.x + BOUND_TEXT_PADDING);
expect(text.height).toBe(APPROX_LINE_HEIGHT);
expect(text.width).toBe(rectangle.width - BOUND_TEXT_PADDING * 2);
});
it("should unbind bound text when unbind action from context menu is triggered", async () => {
expect(h.elements.length).toBe(1);
expect(h.elements[0].id).toBe(rectangle.id);
Keyboard.withModifierKeys({}, () => {
Keyboard.keyPress(KEYS.ENTER);
});
expect(h.elements.length).toBe(2);
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.containerId).toBe(rectangle.id);
const editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
await new Promise((r) => setTimeout(r, 0));
fireEvent.change(editor, { target: { value: "Hello World!" } });
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
mouse.reset();
UI.clickTool("selection");
mouse.clickAt(10, 20);
mouse.down();
mouse.up();
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 20,
clientY: 30,
});
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Unbind text")!);
expect(h.elements[0].boundElements).toEqual([]);
expect((h.elements[1] as ExcalidrawTextElement).containerId).toEqual(
null,
);
});
it("shouldn't bind to container if container has bound text", async () => {
expect(h.elements.length).toBe(1);
Keyboard.withModifierKeys({}, () => {
Keyboard.keyPress(KEYS.ENTER);
});
expect(h.elements.length).toBe(2);
// Bind first text
let text = h.elements[1] as ExcalidrawTextElementWithContainer;
expect(text.containerId).toBe(rectangle.id);
let editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
await new Promise((r) => setTimeout(r, 0));
fireEvent.change(editor, { target: { value: "Hello World!" } });
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: text.id, type: "text" },
]);
// Attempt to bind another text
UI.clickTool("text");
mouse.clickAt(
rectangle.x + rectangle.width / 2,
rectangle.y + rectangle.height / 2,
);
mouse.down();
expect(h.elements.length).toBe(3);
text = h.elements[2] as ExcalidrawTextElementWithContainer;
editor = document.querySelector(
".excalidraw-textEditorContainer > textarea",
) as HTMLTextAreaElement;
await new Promise((r) => setTimeout(r, 0));
fireEvent.change(editor, { target: { value: "Whats up?" } });
editor.blur();
expect(rectangle.boundElements).toStrictEqual([
{ id: h.elements[1].id, type: "text" },
]);
expect(text.containerId).toBe(null);
});
});
});
Example #20
Source File: excalidraw.test.tsx From excalidraw with MIT License | 4 votes |
describe("<Excalidraw/>", () => {
describe("Test zenModeEnabled prop", () => {
it('should show exit zen mode button when zen mode is set and zen mode option in context menu when zenModeEnabled is "undefined"', async () => {
const { container } = await render(<Excalidraw />);
expect(
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(0);
expect(h.state.zenModeEnabled).toBe(false);
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 1,
clientY: 1,
});
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Zen mode")!);
expect(h.state.zenModeEnabled).toBe(true);
expect(
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(1);
});
it("should not show exit zen mode button and zen mode option in context menu when zenModeEnabled is set", async () => {
const { container } = await render(<Excalidraw zenModeEnabled={true} />);
expect(
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(0);
expect(h.state.zenModeEnabled).toBe(true);
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 1,
clientY: 1,
});
const contextMenu = document.querySelector(".context-menu");
expect(queryByText(contextMenu as HTMLElement, "Zen mode")).toBe(null);
expect(h.state.zenModeEnabled).toBe(true);
expect(
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(0);
});
});
describe("Test gridModeEnabled prop", () => {
it('should show grid mode in context menu when gridModeEnabled is "undefined"', async () => {
const { container } = await render(<Excalidraw />);
expect(h.state.gridSize).toBe(null);
expect(
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(0);
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 1,
clientY: 1,
});
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Show grid")!);
expect(h.state.gridSize).toBe(GRID_SIZE);
});
it('should not show grid mode in context menu when gridModeEnabled is not "undefined"', async () => {
const { container } = await render(
<Excalidraw gridModeEnabled={false} />,
);
expect(h.state.gridSize).toBe(null);
expect(
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(0);
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 1,
clientY: 1,
});
const contextMenu = document.querySelector(".context-menu");
expect(queryByText(contextMenu as HTMLElement, "Show grid")).toBe(null);
expect(h.state.gridSize).toBe(null);
});
});
describe("Test theme prop", () => {
it('should show the dark mode toggle when the theme prop is "undefined"', async () => {
const { container } = await render(<Excalidraw />);
expect(h.state.theme).toBe(THEME.LIGHT);
const darkModeToggle = queryByTestId(container, "toggle-dark-mode");
expect(darkModeToggle).toBeTruthy();
});
it('should not show the dark mode toggle when the theme prop is not "undefined"', async () => {
const { container } = await render(<Excalidraw theme="dark" />);
expect(h.state.theme).toBe(THEME.DARK);
expect(queryByTestId(container, "toggle-dark-mode")).toBe(null);
});
});
describe("Test name prop", () => {
it('should allow editing name when the name prop is "undefined"', async () => {
const { container } = await render(<Excalidraw />);
fireEvent.click(queryByTestId(container, "image-export-button")!);
const textInput: HTMLInputElement | null = document.querySelector(
".ExportDialog .ProjectName .TextInput",
);
expect(textInput?.value).toContain(`${t("labels.untitled")}`);
expect(textInput?.nodeName).toBe("INPUT");
});
it('should set the name and not allow editing when the name prop is present"', async () => {
const name = "test";
const { container } = await render(<Excalidraw name={name} />);
await fireEvent.click(queryByTestId(container, "image-export-button")!);
const textInput = document.querySelector(
".ExportDialog .ProjectName .TextInput--readonly",
);
expect(textInput?.textContent).toEqual(name);
expect(textInput?.nodeName).toBe("SPAN");
});
});
describe("Test UIOptions prop", () => {
it('should not hide any UI element when the UIOptions prop is "undefined"', async () => {
await render(<Excalidraw />);
const canvasActions = document.querySelector(
'section[aria-labelledby="test-id-canvasActions-title"]',
);
expect(canvasActions).toMatchSnapshot();
});
describe("Test canvasActions", () => {
it('should not hide any UI element when canvasActions is "undefined"', async () => {
await render(<Excalidraw UIOptions={{}} />);
const canvasActions = document.querySelector(
'section[aria-labelledby="test-id-canvasActions-title"]',
);
expect(canvasActions).toMatchSnapshot();
});
it("should hide clear canvas button when clearCanvas is false", async () => {
const { container } = await render(
<Excalidraw UIOptions={{ canvasActions: { clearCanvas: false } }} />,
);
expect(queryByTestId(container, "clear-canvas-button")).toBeNull();
});
it("should hide export button when export is false", async () => {
const { container } = await render(
<Excalidraw UIOptions={{ canvasActions: { export: false } }} />,
);
expect(queryByTestId(container, "json-export-button")).toBeNull();
});
it("should hide 'Save as image' button when 'saveAsImage' is false", async () => {
const { container } = await render(
<Excalidraw UIOptions={{ canvasActions: { saveAsImage: false } }} />,
);
expect(queryByTestId(container, "image-export-button")).toBeNull();
});
it("should hide load button when loadScene is false", async () => {
const { container } = await render(
<Excalidraw UIOptions={{ canvasActions: { loadScene: false } }} />,
);
expect(queryByTestId(container, "load-button")).toBeNull();
});
it("should hide save as button when saveFileToDisk is false", async () => {
const { container } = await render(
<Excalidraw
UIOptions={{ canvasActions: { export: { saveFileToDisk: false } } }}
/>,
);
expect(queryByTestId(container, "save-as-button")).toBeNull();
});
it("should hide save button when saveToActiveFile is false", async () => {
const { container } = await render(
<Excalidraw
UIOptions={{ canvasActions: { saveToActiveFile: false } }}
/>,
);
expect(queryByTestId(container, "save-button")).toBeNull();
});
it("should hide the canvas background picker when changeViewBackgroundColor is false", async () => {
const { container } = await render(
<Excalidraw
UIOptions={{ canvasActions: { changeViewBackgroundColor: false } }}
/>,
);
expect(queryByTestId(container, "canvas-background-picker")).toBeNull();
});
it("should hide the theme toggle when theme is false", async () => {
const { container } = await render(
<Excalidraw UIOptions={{ canvasActions: { theme: false } }} />,
);
expect(queryByTestId(container, "toggle-dark-mode")).toBeNull();
});
});
});
describe("Test autoFocus prop", () => {
it("should not focus when autoFocus is false", async () => {
const { container } = await render(<Excalidraw />);
expect(
container.querySelector(".excalidraw") === document.activeElement,
).toBe(false);
});
it("should focus when autoFocus is true", async () => {
const { container } = await render(<Excalidraw autoFocus={true} />);
expect(
container.querySelector(".excalidraw") === document.activeElement,
).toBe(true);
});
});
});
Example #21
Source File: regressionTests.test.tsx From excalidraw-embed with MIT License | 4 votes |
describe("regression tests", () => {
it("draw every type of shape", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("diamond");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("ellipse");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("arrow");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("line");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("arrow");
mouse.click(10, -10);
mouse.click(10, 10);
mouse.click(-10, 10);
hotkeyPress("ENTER");
clickTool("line");
mouse.click(10, -20);
mouse.click(10, 10);
mouse.click(-10, 10);
hotkeyPress("ENTER");
clickTool("draw");
mouse.down(10, -20);
mouse.up(10, 10);
expect(h.elements.map((element) => element.type)).toEqual([
"rectangle",
"diamond",
"ellipse",
"arrow",
"line",
"arrow",
"line",
"draw",
]);
});
it("click to select a shape", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
const firstRectPos = mouse.getPosition();
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
const prevSelectedId = getSelectedElement().id;
mouse.restorePosition(...firstRectPos);
mouse.click();
expect(getSelectedElement().id).not.toEqual(prevSelectedId);
});
for (const [keys, shape] of [
["2r", "rectangle"],
["3d", "diamond"],
["4e", "ellipse"],
["5a", "arrow"],
["6l", "line"],
["7x", "draw"],
] as [string, ExcalidrawElement["type"]][]) {
for (const key of keys) {
it(`hotkey ${key} selects ${shape} tool`, () => {
keyPress(key);
mouse.down(10, 10);
mouse.up(10, 10);
expect(getSelectedElement().type).toBe(shape);
});
}
}
it("change the properties of a shape", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickLabeledElement("Background");
clickLabeledElement("#fa5252");
clickLabeledElement("Stroke");
clickLabeledElement("#5f3dc4");
expect(getSelectedElement().backgroundColor).toBe("#fa5252");
expect(getSelectedElement().strokeColor).toBe("#5f3dc4");
});
it("resize an element, trying every resize handle", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
const resizeHandles = getResizeHandles("mouse");
delete resizeHandles.rotation; // exclude rotation handle
for (const handlePos in resizeHandles) {
const [x, y] = resizeHandles[handlePos as keyof typeof resizeHandles];
const { width: prevWidth, height: prevHeight } = getSelectedElement();
mouse.restorePosition(x, y);
mouse.down();
mouse.up(-5, -5);
const {
width: nextWidthNegative,
height: nextHeightNegative,
} = getSelectedElement();
expect(
prevWidth !== nextWidthNegative || prevHeight !== nextHeightNegative,
).toBeTruthy();
checkpoint(`resize handle ${handlePos} (-5, -5)`);
mouse.down();
mouse.up(5, 5);
const { width, height } = getSelectedElement();
expect(width).toBe(prevWidth);
expect(height).toBe(prevHeight);
checkpoint(`unresize handle ${handlePos} (-5, -5)`);
mouse.restorePosition(x, y);
mouse.down();
mouse.up(5, 5);
const {
width: nextWidthPositive,
height: nextHeightPositive,
} = getSelectedElement();
expect(
prevWidth !== nextWidthPositive || prevHeight !== nextHeightPositive,
).toBeTruthy();
checkpoint(`resize handle ${handlePos} (+5, +5)`);
mouse.down();
mouse.up(-5, -5);
const { width: finalWidth, height: finalHeight } = getSelectedElement();
expect(finalWidth).toBe(prevWidth);
expect(finalHeight).toBe(prevHeight);
checkpoint(`unresize handle ${handlePos} (+5, +5)`);
}
});
it("click on an element and drag it", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
const { x: prevX, y: prevY } = getSelectedElement();
mouse.down(-10, -10);
mouse.up(10, 10);
const { x: nextX, y: nextY } = getSelectedElement();
expect(nextX).toBeGreaterThan(prevX);
expect(nextY).toBeGreaterThan(prevY);
checkpoint("dragged");
mouse.down();
mouse.up(-10, -10);
const { x, y } = getSelectedElement();
expect(x).toBe(prevX);
expect(y).toBe(prevY);
});
it("alt-drag duplicates an element", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
expect(
h.elements.filter((element) => element.type === "rectangle").length,
).toBe(1);
withModifierKeys({ alt: true }, () => {
mouse.down(-10, -10);
mouse.up(10, 10);
});
expect(
h.elements.filter((element) => element.type === "rectangle").length,
).toBe(2);
});
it("click-drag to select a group", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
const finalPosition = mouse.getPosition();
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
mouse.restorePosition(0, 0);
mouse.down();
mouse.restorePosition(...finalPosition);
mouse.up(5, 5);
expect(
h.elements.filter((element) => h.state.selectedElementIds[element.id])
.length,
).toBe(2);
});
it("shift-click to multiselect, then drag", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
const prevRectsXY = h.elements
.filter((element) => element.type === "rectangle")
.map((element) => ({ x: element.x, y: element.y }));
mouse.reset();
mouse.click(10, 10);
withModifierKeys({ shift: true }, () => {
mouse.click(20, 0);
});
mouse.down();
mouse.up(10, 10);
h.elements
.filter((element) => element.type === "rectangle")
.forEach((element, i) => {
expect(element.x).toBeGreaterThan(prevRectsXY[i].x);
expect(element.y).toBeGreaterThan(prevRectsXY[i].y);
});
});
it("pinch-to-zoom works", () => {
expect(h.state.zoom).toBe(1);
finger1.down(50, 50);
finger2.down(60, 50);
finger1.move(-10, 0);
expect(h.state.zoom).toBeGreaterThan(1);
const zoomed = h.state.zoom;
finger1.move(5, 0);
finger2.move(-5, 0);
expect(h.state.zoom).toBeLessThan(zoomed);
});
it("two-finger scroll works", () => {
const startScrollY = h.state.scrollY;
finger1.down(50, 50);
finger2.down(60, 50);
finger1.up(0, -10);
finger2.up(0, -10);
expect(h.state.scrollY).toBeLessThan(startScrollY);
const startScrollX = h.state.scrollX;
finger1.restorePosition(50, 50);
finger2.restorePosition(50, 60);
finger1.down();
finger2.down();
finger1.up(10, 0);
finger2.up(10, 0);
expect(h.state.scrollX).toBeGreaterThan(startScrollX);
});
it("spacebar + drag scrolls the canvas", () => {
const { scrollX: startScrollX, scrollY: startScrollY } = h.state;
hotkeyDown("SPACE");
mouse.down(50, 50);
mouse.up(60, 60);
hotkeyUp("SPACE");
const { scrollX, scrollY } = h.state;
expect(scrollX).not.toEqual(startScrollX);
expect(scrollY).not.toEqual(startScrollY);
});
it("arrow keys", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
hotkeyPress("ARROW_LEFT");
hotkeyPress("ARROW_LEFT");
hotkeyPress("ARROW_RIGHT");
hotkeyPress("ARROW_UP");
hotkeyPress("ARROW_UP");
hotkeyPress("ARROW_DOWN");
expect(h.elements[0].x).toBe(9);
expect(h.elements[0].y).toBe(9);
});
it("undo/redo drawing an element", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("arrow");
mouse.click(10, -10);
mouse.click(10, 10);
mouse.click(-10, 10);
hotkeyPress("ENTER");
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(3);
withModifierKeys({ ctrl: true }, () => {
keyPress("z");
keyPress("z");
});
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(2);
withModifierKeys({ ctrl: true }, () => {
keyPress("z");
});
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(1);
withModifierKeys({ ctrl: true, shift: true }, () => {
keyPress("z");
});
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(2);
});
it("noop interaction after undo shouldn't create history entry", () => {
// NOTE: this will fail if this test case is run in isolation. There's
// some leaking state or race conditions in initialization/teardown
// (couldn't figure out)
expect(getStateHistory().length).toBe(0);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
const firstElementEndPoint = mouse.getPosition();
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
const secondElementEndPoint = mouse.getPosition();
expect(getStateHistory().length).toBe(2);
withModifierKeys({ ctrl: true }, () => {
keyPress("z");
});
expect(getStateHistory().length).toBe(1);
// clicking an element shouldn't add to history
mouse.restorePosition(...firstElementEndPoint);
mouse.click();
expect(getStateHistory().length).toBe(1);
withModifierKeys({ shift: true, ctrl: true }, () => {
keyPress("z");
});
expect(getStateHistory().length).toBe(2);
// clicking an element shouldn't add to history
mouse.click();
expect(getStateHistory().length).toBe(2);
const firstSelectedElementId = getSelectedElement().id;
// same for clicking the element just redo-ed
mouse.restorePosition(...secondElementEndPoint);
mouse.click();
expect(getStateHistory().length).toBe(2);
expect(getSelectedElement().id).not.toEqual(firstSelectedElementId);
});
it("zoom hotkeys", () => {
expect(h.state.zoom).toBe(1);
fireEvent.keyDown(document, { code: "Equal", ctrlKey: true });
fireEvent.keyUp(document, { code: "Equal", ctrlKey: true });
expect(h.state.zoom).toBeGreaterThan(1);
fireEvent.keyDown(document, { code: "Minus", ctrlKey: true });
fireEvent.keyUp(document, { code: "Minus", ctrlKey: true });
expect(h.state.zoom).toBe(1);
});
it("rerenders UI on language change", async () => {
// select rectangle tool to show properties menu
clickTool("rectangle");
// english lang should display `hachure` label
expect(screen.queryByText(/hachure/i)).not.toBeNull();
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
target: { value: "de-DE" },
});
// switching to german, `hachure` label should no longer exist
await waitFor(() => expect(screen.queryByText(/hachure/i)).toBeNull());
// reset language
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
target: { value: "en" },
});
// switching back to English
await waitFor(() => expect(screen.queryByText(/hachure/i)).not.toBeNull());
});
it("make a group and duplicate it", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
const end = mouse.getPosition();
mouse.reset();
mouse.down();
mouse.restorePosition(...end);
mouse.up();
expect(h.elements.length).toBe(3);
for (const element of h.elements) {
expect(element.groupIds.length).toBe(0);
expect(h.state.selectedElementIds[element.id]).toBe(true);
}
withModifierKeys({ ctrl: true }, () => {
keyPress("g");
});
for (const element of h.elements) {
expect(element.groupIds.length).toBe(1);
}
withModifierKeys({ alt: true }, () => {
mouse.restorePosition(...end);
mouse.down();
mouse.up(10, 10);
});
expect(h.elements.length).toBe(6);
const groups = new Set();
for (const element of h.elements) {
for (const groupId of element.groupIds) {
groups.add(groupId);
}
}
expect(groups.size).toBe(2);
});
it("double click to edit a group", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
withModifierKeys({ ctrl: true }, () => {
keyPress("a");
keyPress("g");
});
expect(getSelectedElements().length).toBe(3);
expect(h.state.editingGroupId).toBe(null);
mouse.doubleClick();
expect(getSelectedElements().length).toBe(1);
expect(h.state.editingGroupId).not.toBe(null);
});
it("adjusts z order when grouping", () => {
const positions = [];
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
positions.push(mouse.getPosition());
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
positions.push(mouse.getPosition());
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
positions.push(mouse.getPosition());
const ids = h.elements.map((element) => element.id);
mouse.restorePosition(...positions[0]);
mouse.click();
mouse.restorePosition(...positions[2]);
withModifierKeys({ shift: true }, () => {
mouse.click();
});
withModifierKeys({ ctrl: true }, () => {
keyPress("g");
});
expect(h.elements.map((element) => element.id)).toEqual([
ids[1],
ids[0],
ids[2],
]);
});
it("supports nested groups", () => {
const positions: number[][] = [];
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
positions.push(mouse.getPosition());
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
positions.push(mouse.getPosition());
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
positions.push(mouse.getPosition());
withModifierKeys({ ctrl: true }, () => {
keyPress("a");
keyPress("g");
});
mouse.doubleClick();
withModifierKeys({ shift: true }, () => {
mouse.restorePosition(...positions[0]);
mouse.click();
});
withModifierKeys({ ctrl: true }, () => {
keyPress("g");
});
const groupIds = h.elements[2].groupIds;
expect(groupIds.length).toBe(2);
expect(h.elements[1].groupIds).toEqual(groupIds);
expect(h.elements[0].groupIds).toEqual(groupIds.slice(1));
mouse.click(50, 50);
expect(getSelectedElements().length).toBe(0);
mouse.restorePosition(...positions[0]);
mouse.click();
expect(getSelectedElements().length).toBe(3);
expect(h.state.editingGroupId).toBe(null);
mouse.doubleClick();
expect(getSelectedElements().length).toBe(2);
expect(h.state.editingGroupId).toBe(groupIds[1]);
mouse.doubleClick();
expect(getSelectedElements().length).toBe(1);
expect(h.state.editingGroupId).toBe(groupIds[0]);
// click out of the group
mouse.restorePosition(...positions[1]);
mouse.click();
expect(getSelectedElements().length).toBe(0);
mouse.click();
expect(getSelectedElements().length).toBe(3);
mouse.doubleClick();
expect(getSelectedElements().length).toBe(1);
});
it("updates fontSize & fontFamily appState", () => {
clickTool("text");
expect(h.state.currentItemFontFamily).toEqual(1); // Virgil
fireEvent.click(screen.getByText(/code/i));
expect(h.state.currentItemFontFamily).toEqual(3); // Cascadia
});
it("shows context menu for canvas", () => {
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
const options = contextMenu?.querySelectorAll(".context-menu-option");
const expectedOptions = ["Select all", "Toggle grid mode"];
expect(contextMenu).not.toBeNull();
expect(options?.length).toBe(2);
expect(options?.item(0).textContent).toBe(expectedOptions[0]);
});
it("shows context menu for element", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
const options = contextMenu?.querySelectorAll(".context-menu-option");
const expectedOptions = [
"Copy styles",
"Paste styles",
"Delete",
"Add to library",
"Send backward",
"Bring forward",
"Send to back",
"Bring to front",
"Duplicate",
];
expect(contextMenu).not.toBeNull();
expect(contextMenu?.children.length).toBe(9);
options?.forEach((opt, i) => {
expect(opt.textContent).toBe(expectedOptions[i]);
});
});
it("shows 'Group selection' in context menu for multiple selected elements", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
mouse.reset();
mouse.click(10, 10);
withModifierKeys({ shift: true }, () => {
mouse.click(20, 0);
});
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
const options = contextMenu?.querySelectorAll(".context-menu-option");
const expectedOptions = [
"Copy styles",
"Paste styles",
"Delete",
"Group selection",
"Add to library",
"Send backward",
"Bring forward",
"Send to back",
"Bring to front",
"Duplicate",
];
expect(contextMenu).not.toBeNull();
expect(contextMenu?.children.length).toBe(10);
options?.forEach((opt, i) => {
expect(opt.textContent).toBe(expectedOptions[i]);
});
});
it("shows 'Ungroup selection' in context menu for group inside selected elements", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
clickTool("rectangle");
mouse.down(10, -10);
mouse.up(10, 10);
mouse.reset();
mouse.click(10, 10);
withModifierKeys({ shift: true }, () => {
mouse.click(20, 0);
});
withModifierKeys({ ctrl: true }, () => {
keyPress("g");
});
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
const options = contextMenu?.querySelectorAll(".context-menu-option");
const expectedOptions = [
"Copy styles",
"Paste styles",
"Delete",
"Ungroup selection",
"Add to library",
"Send backward",
"Bring forward",
"Send to back",
"Bring to front",
"Duplicate",
];
expect(contextMenu).not.toBeNull();
expect(contextMenu?.children.length).toBe(10);
options?.forEach((opt, i) => {
expect(opt.textContent).toBe(expectedOptions[i]);
});
});
it("selecting 'Copy styles' in context menu copies styles", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
expect(copiedStyles).toBe("{}");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!);
expect(copiedStyles).not.toBe("{}");
const element = JSON.parse(copiedStyles);
expect(element).toEqual(getSelectedElement());
});
it("selecting 'Paste styles' in context menu pastes styles", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
// Change some styles of second rectangle
clickLabeledElement("Stroke");
clickLabeledElement("#c92a2a");
clickLabeledElement("Background");
clickLabeledElement("#e64980");
// Fill style
fireEvent.click(screen.getByLabelText("Cross-hatch"));
// Stroke width
fireEvent.click(screen.getByLabelText("Bold"));
// Stroke style
fireEvent.click(screen.getByLabelText("Dotted"));
// Roughness
fireEvent.click(screen.getByLabelText("Cartoonist"));
// Opacity
fireEvent.change(screen.getByLabelText("Opacity"), {
target: { value: "60" },
});
mouse.reset();
// Copy styles of second rectangle
fireEvent.contextMenu(canvas, { button: 2, clientX: 40, clientY: 40 });
let contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!);
const secondRect = JSON.parse(copiedStyles);
expect(secondRect.id).toBe(h.elements[1].id);
mouse.reset();
// Paste styles to first rectangle
fireEvent.contextMenu(canvas, { button: 2, clientX: 10, clientY: 10 });
contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Paste styles")!);
const firstRect = getSelectedElement();
expect(firstRect.id).toBe(h.elements[0].id);
expect(firstRect.strokeColor).toBe("#c92a2a");
expect(firstRect.backgroundColor).toBe("#e64980");
expect(firstRect.fillStyle).toBe("cross-hatch");
expect(firstRect.strokeWidth).toBe(2); // Bold: 2
expect(firstRect.strokeStyle).toBe("dotted");
expect(firstRect.roughness).toBe(2); // Cartoonist: 2
expect(firstRect.opacity).toBe(60);
});
it("selecting 'Delete' in context menu deletes element", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Delete")!);
expect(getSelectedElements()).toHaveLength(0);
expect(h.elements[0].isDeleted).toBe(true);
});
it("selecting 'Add to library' in context menu adds element to library", async () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Add to library")!);
await waitFor(() => {
const library = localStorage.getItem("excalidraw-library");
expect(library).not.toBeNull();
const addedElement = JSON.parse(library!)[0][0];
expect(addedElement).toEqual(h.elements[0]);
});
});
it("selecting 'Duplicate' in context menu duplicates element", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(queryByText(contextMenu as HTMLElement, "Duplicate")!);
expect(h.elements).toHaveLength(2);
const { id: _id0, seed: _seed0, x: _x0, y: _y0, ...rect1 } = h.elements[0];
const { id: _id1, seed: _seed1, x: _x1, y: _y1, ...rect2 } = h.elements[1];
expect(rect1).toEqual(rect2);
});
it("selecting 'Send backward' in context menu sends element backward", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
mouse.reset();
fireEvent.contextMenu(canvas, { button: 2, clientX: 40, clientY: 40 });
const contextMenu = document.querySelector(".context-menu");
const elementsBefore = h.elements;
fireEvent.click(queryByText(contextMenu as HTMLElement, "Send backward")!);
expect(elementsBefore[0].id).toEqual(h.elements[1].id);
expect(elementsBefore[1].id).toEqual(h.elements[0].id);
});
it("selecting 'Bring forward' in context menu brings element forward", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
mouse.reset();
fireEvent.contextMenu(canvas, { button: 2, clientX: 10, clientY: 10 });
const contextMenu = document.querySelector(".context-menu");
const elementsBefore = h.elements;
fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring forward")!);
expect(elementsBefore[0].id).toEqual(h.elements[1].id);
expect(elementsBefore[1].id).toEqual(h.elements[0].id);
});
it("selecting 'Send to back' in context menu sends element to back", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
mouse.reset();
fireEvent.contextMenu(canvas, { button: 2, clientX: 40, clientY: 40 });
const contextMenu = document.querySelector(".context-menu");
const elementsBefore = h.elements;
fireEvent.click(queryByText(contextMenu as HTMLElement, "Send to back")!);
expect(elementsBefore[1].id).toEqual(h.elements[0].id);
});
it("selecting 'Bring to front' in context menu brings element to front", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
mouse.reset();
fireEvent.contextMenu(canvas, { button: 2, clientX: 10, clientY: 10 });
const contextMenu = document.querySelector(".context-menu");
const elementsBefore = h.elements;
fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring to front")!);
expect(elementsBefore[0].id).toEqual(h.elements[1].id);
});
it("selecting 'Group selection' in context menu groups selected elements", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
mouse.reset();
withModifierKeys({ shift: true }, () => {
mouse.click(10, 10);
});
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(
queryByText(contextMenu as HTMLElement, "Group selection")!,
);
const selectedGroupIds = Object.keys(h.state.selectedGroupIds);
expect(h.elements[0].groupIds).toEqual(selectedGroupIds);
expect(h.elements[1].groupIds).toEqual(selectedGroupIds);
});
it("selecting 'Ungroup selection' in context menu ungroups selected group", () => {
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
mouse.reset();
withModifierKeys({ shift: true }, () => {
mouse.click(10, 10);
});
withModifierKeys({ ctrl: true }, () => {
keyPress("g");
});
fireEvent.contextMenu(canvas, { button: 2, clientX: 1, clientY: 1 });
const contextMenu = document.querySelector(".context-menu");
fireEvent.click(
queryByText(contextMenu as HTMLElement, "Ungroup selection")!,
);
const selectedGroupIds = Object.keys(h.state.selectedGroupIds);
expect(selectedGroupIds).toHaveLength(0);
expect(h.elements[0].groupIds).toHaveLength(0);
expect(h.elements[1].groupIds).toHaveLength(0);
});
});
Example #22
Source File: UMLEditor.test.tsx From legend-studio with Apache License 2.0 | 4 votes |
test(
integrationTest('Class editor without constraints and derived properties'),
async () => {
await TEST__openElementFromExplorerTree('ui::TestClass', renderResult);
const editPanelHeader = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.EDIT_PANEL__HEADER_TABS,
);
expect(getByText(editPanelHeader, 'TestClass')).not.toBeNull();
const classForm = renderResult.getByTestId(
LEGEND_STUDIO_TEST_ID.CLASS_FORM_EDITOR,
);
// Normal properties
const classProperties = ['a', 'b', 'name', 'person'];
classProperties.forEach((t) =>
expect(getByDisplayValue(classForm, t)).not.toBeNull(),
);
// Supertype propertes
const superTypeProperties = [
'legs',
'arms',
'planet',
'description',
'founder',
];
superTypeProperties.forEach((superTypeProperty) => {
// input fields for super type property name are not present/disabled
expect(queryByDisplayValue(classForm, superTypeProperty)).toBeNull();
expect(queryByText(classForm, superTypeProperty)).not.toBeNull();
});
// Association properties
const associationProperties = ['testClassSibling'];
associationProperties.forEach((associationProperty) => {
// input fields for association property name are not present/disabled
expect(queryByDisplayValue(classForm, associationProperty)).toBeNull();
expect(queryByText(classForm, associationProperty)).not.toBeNull();
});
// SuperTypes
fireEvent.click(getByText(classForm, 'Super Types'));
await waitFor(() => getByText(classForm, 'Animal'));
// TaggedValues
fireEvent.click(getByText(classForm, 'Tagged Values'));
await waitFor(() => getByText(classForm, 'ProfileTest'));
expect(getByText(classForm, 'tag1')).not.toBeNull();
expect(getByDisplayValue(classForm, 'test')).not.toBeNull();
// Stereotypes
fireEvent.click(getByText(classForm, 'Stereotypes'));
await waitFor(() => getByText(classForm, 'ProfileTest'));
expect(getByText(classForm, 'stereotype1')).not.toBeNull();
// Back to properties. Test more rigorous
fireEvent.click(getByText(classForm, 'Properties'));
await waitFor(() => getByText(classForm, 'founder'));
const inputA = getByDisplayValue(classForm, 'a');
const propertyA = inputA.parentElement?.parentElement
?.parentElement as HTMLElement;
fireEvent.change(inputA, { target: { value: 'abcdefg' } });
await waitFor(() => getByDisplayValue(classForm, 'abcdefg'));
expect(getAllByDisplayValue(propertyA, '1')).toHaveLength(2);
expect(getByText(propertyA, 'String')).not.toBeNull();
expect(getAllByRole(propertyA, 'button')).toHaveLength(2);
fireEvent.click(guaranteeNonNullable(getAllByRole(propertyA, 'button')[1]));
expect(queryByDisplayValue(classForm, 'abcdefg')).toBeNull();
// Sub Panel Property
const inputB = getByDisplayValue(classForm, 'b');
const propertyB = inputB.parentElement?.parentElement
?.parentElement as HTMLElement;
const buttons = getAllByRole(propertyB, 'button');
expect(buttons).toHaveLength(2);
expect(queryByDisplayValue(classForm, 'ProfileTest')).toBeNull();
const navigateToPropertyButton = guaranteeNonNullable(buttons[0]);
fireEvent.click(navigateToPropertyButton);
await waitFor(() => getByText(classForm, 'property'));
const subPropertyPanel = getByTestId(
classForm,
LEGEND_STUDIO_TEST_ID.PANEL,
);
expect(
getByDisplayValue(subPropertyPanel, 'lets write a tag'),
).not.toBeNull();
expect(getAllByText(subPropertyPanel, 'tag2')).not.toBeNull();
expect(getByText(subPropertyPanel, 'ProfileTest')).not.toBeNull();
fireEvent.click(getByText(subPropertyPanel, 'Stereotypes'));
await waitFor(() => getByText(subPropertyPanel, 'stereotype1'));
fireEvent.click(
guaranteeNonNullable(getAllByRole(subPropertyPanel, 'button')[0]),
);
expect(queryByRole(classForm, 'panel')).toBeNull();
},
);