@testing-library/react#within JavaScript Examples
The following examples show how to use
@testing-library/react#within.
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: Provider.test.jsx From covid with GNU General Public License v3.0 | 6 votes |
test('a multiple context consumer is rendered properly when rendered inside the multi-provider', async () => {
const rendered = render(
<Provider>
<WrappedChild data-value='test-props' />
</Provider>
)
const child = rendered.getByRole('child')
expect(child).toBeInTheDocument();
['Bcn', 'Map', 'Chart'].forEach((backend) => {
const testBackend = within(child).getByRole(backend)
expect(testBackend).toHaveTextContent(`class ${backend}DataHandler`)
})
})
Example #2
Source File: Skiplinks.test.js From react-dsfr with MIT License | 6 votes |
describe('<SkipLinks />', () => {
it('should renders SkipLinks properly', () => {
render(
<Skiplinks data-testid="skiplinks">
<SkiplinkItem href="#" data-testid="skiplinkitem1">Accéder au contenu</SkiplinkItem>
<SkiplinkItem href="#" data-test-id="skiplinkitem2">Accéder au menu</SkiplinkItem>
<SkiplinkItem href="#" data-test--id="skiplinkitem3">Accéder à la recherche</SkiplinkItem>
<SkiplinkItem href="#" data-test="skiplinkitem4">Accéder au footer</SkiplinkItem>
</Skiplinks>,
);
const skiplinks = screen.getByTestId('skiplinks');
expect(skiplinks).toBeInTheDocument();
expect(skiplinks).toMatchSnapshot();
const links = screen.getByRole('list');
const { getAllByRole } = within(links);
const items = getAllByRole('listitem');
expect(items.length).toBe(4);
});
});
Example #3
Source File: delivery-type.test.js From horondi_client_fe with MIT License | 6 votes |
describe('<DeliveryType />', () => {
it('Radio group should be rendered', () => {
render(<DeliveryType {...props} deliveryType='COURIER' />);
const radiogroup = screen.getByRole('radiogroup', { name: 'Delivery type' });
expect(radiogroup).toBeInTheDocument();
});
it('setDeliveryType function should be called with correct argument', () => {
render(<DeliveryType {...props} deliveryType='NOVAPOST' />);
expect(props.setDeliveryType).toHaveBeenCalledWith('NOVAPOST');
});
it('handleCourierOrganizationChange function should triggered on select change', () => {
render(<DeliveryType {...props} deliveryType='COURIER' />);
const selectWrapper = screen.getByTestId('courierOrganization');
const selectComponent = within(selectWrapper).getByRole('button');
fireEvent.mouseDown(selectComponent);
const popup = within(screen.getByRole('listbox'));
fireEvent.click(popup.getAllByRole('option')[0]);
});
});
Example #4
Source File: worldwide.spec.js From horondi_client_fe with MIT License | 6 votes |
describe('tests for worldwide delivery component', () => {
it('worldwide delivery component should be rendered correctly', () => {
render(<Worldwide {...props} />);
const heading = screen.getByRole('heading', { level: 3 });
const statesWrapper = screen.getByTestId('stateOrProvince');
const statesInput = within(statesWrapper).getByRole('textbox');
expect(heading).toBeInTheDocument();
expect(statesInput).toHaveAttribute('disabled');
});
it('test typing in worldwideCity input', () => {
render(<Worldwide {...props} values={{ worldWideCountry: 'Ukraine' }} />);
const citiesWrapper = screen.getByTestId('worldWideCity');
const citiesInput = within(citiesWrapper).getByRole('textbox');
fireEvent.change(citiesInput, { target: { value: 'city' } });
expect(citiesInput).toHaveAttribute('value', 'city');
});
});
Example #5
Source File: delivery.spec.js From horondi_admin with MIT License | 6 votes |
describe('tests for delivery component', () => {
it('should render delivery component with worldwide fields', () => {
render(
<Delivery
setFieldValue={setFieldValue}
data={{ delivery: { ...deliveryProps, sentBy: 'WORLDWIDE' } }}
/>
);
const statesWrapper = screen.getByTestId('stateOrProvince');
const statesInput = within(statesWrapper).getByRole('textbox');
expect(statesInput).toHaveAttribute('disabled');
});
it('should render delivery component with ukrpost courier or novapost courier fields', () => {
render(
<Delivery
setFieldValue={setFieldValue}
data={{ delivery: { ...deliveryProps, sentBy: 'UKRPOSTCOURIER' } }}
/>
);
const cityInputWrapper = screen.getByTestId('delivery.courier.city');
const cityInput = within(cityInputWrapper).getByRole('textbox');
expect(cityInput).toBeInTheDocument();
});
});
Example #6
Source File: worldwide.spec.js From horondi_admin with MIT License | 6 votes |
describe('tests for worldwide delivery component', () => {
it('worldwide delivery component should be rendered correctly', () => {
render(<Worldwide {...props} setFieldValue={setFieldValue} />);
const heading = screen.getByRole('heading', { level: 3 });
const statesWrapper = screen.getByTestId('stateOrProvince');
const statesInput = within(statesWrapper).getByRole('textbox');
expect(heading).toBeInTheDocument();
expect(statesInput).toHaveAttribute('disabled');
});
it('test typing in worldwideCity input', () => {
render(
<Worldwide
{...props}
values={{ worldWideCountry: 'Ukraine' }}
setFieldValue={setFieldValue}
/>
);
const citiesWrapper = screen.getByTestId('worldWideCity');
const citiesInput = within(citiesWrapper).getByRole('textbox');
fireEvent.change(citiesInput, { target: { value: 'city' } });
expect(citiesInput).toHaveAttribute('value', 'city');
});
});
Example #7
Source File: Pagination.test.js From react-dsfr with MIT License | 6 votes |
describe('<Pagination />', () => {
it('should render pagination properly', () => {
render(
<Pagination buildURL={(page) => `page${page}`} currentPage={8} pageCount={15} data-testid="pagination" />,
);
const pagination = screen.getByTestId('pagination');
expect(pagination.className).toBe('fr-pagination');
const pages = screen.getByRole('list');
const { getAllByRole } = within(pages);
const items = getAllByRole('listitem');
expect(items.length).toBe(13);
expect(pagination).toMatchSnapshot();
});
it('should render state pagination properly', () => {
render(
<Pagination onClick={() => {}} currentPage={8} pageCount={15} data-testid="pagination" />,
);
const pagination = screen.getByTestId('pagination');
expect(pagination.className).toBe('fr-pagination');
const pages = screen.getByRole('list');
const { getAllByRole } = within(pages);
const items = getAllByRole('listitem');
expect(items.length).toBe(13);
expect(pagination).toMatchSnapshot();
});
});
Example #8
Source File: App.test.js From HackerRank-React-Basic with MIT License | 6 votes |
expectArticles = (articles, expectedArticles) => {
expect(articles).toHaveLength(expectedArticles.length);
articles.forEach((article, i) => {
const title = within(article).getByTestId("article-title").textContent;
const upvotes = within(article).getByTestId("article-upvotes").textContent;
const date = within(article).getByTestId("article-date").textContent;
const expectedArticle = expectedArticles[i];
expect([title, upvotes, date]).toEqual([expectedArticle.title, expectedArticle.upvotes.toString(), expectedArticle.date]);
});
}
Example #9
Source File: setup.js From ui-data-export with Apache License 2.0 | 6 votes |
checkJobProfileFormState = async (form, { title }) => {
const formTitle = await within(form).findByText(title);
const nameInput = await within(form).findByLabelText(/Name/i);
const mappingProfileInput = await within(form).findByLabelText(/Mapping profile/i);
const descriptionInput = await within(form).findByLabelText('Description');
expect(form).toBeVisible();
expect(formTitle).toBeVisible();
expect(nameInput).toBeVisible();
expect(mappingProfileInput).toBeVisible();
expect(descriptionInput).toBeVisible();
expect(nameInput).toBeEnabled();
expect(descriptionInput).toBeEnabled();
expect(mappingProfileInput).toBeEnabled();
return {
formTitle,
nameInput,
mappingProfileInput,
descriptionInput,
};
}
Example #10
Source File: swap_card.spec.js From astroport-lbp-frontend with MIT License | 6 votes |
async function waitForBalances({ fromBalance, toBalance }) {
const [fromBalanceLabel, toBalanceLabel] = screen.getAllByText('Balance:');
if(fromBalance !== undefined) {
expect(await within(fromBalanceLabel.parentElement).findByText(fromBalance)).toBeInTheDocument();
}
if(toBalance !== undefined) {
expect(await within(toBalanceLabel.parentElement).findByText(toBalance)).toBeInTheDocument();
}
}
Example #11
Source File: setup.js From ui-data-export with Apache License 2.0 | 5 votes |
transformationListCells = () => within(transformationListRows()[0]).getByText('Instance - Resource title')
Example #12
Source File: testingUtility.js From lens-extension-cc with MIT License | 5 votes |
customWithin = function (el) {
const result = within(el);
const boundQueries = getBoundQueries(el);
return { ...result, ...boundQueries };
}
Example #13
Source File: current_token_sale.spec.js From astroport-lbp-frontend with MIT License | 5 votes |
describe('CurrentTokenSale', () => {
it('fetches and displays data for current token sale', async () => {
fetchUSTExchangeRate.mockResolvedValue(0.99);
getWeights.mockResolvedValue([5, 95]);
getPool.mockResolvedValue({
assets: [
{
info: {
native_token: {
denom: 'uusd'
}
},
amount: '5000000000000', // 5,000,000.000000
start_weight: '2',
end_weight: '60'
},
{
info: {
token: {
contract_addr: 'terra123'
}
},
amount: '42000000123456', // 42,000,000.123456
start_weight: '98',
end_weight: '40'
}
],
total_share: '60000000'
});
const pair = buildPair({
contractAddr: 'terra1',
tokenContractAddr: 'terra123',
endTime: Math.floor((new Date(2021, 5, 18, 11, 10)).getTime() / 1000),
description: 'A brand new token for sale!'
});
const saleTokenInfo = {
symbol: 'FOO',
decimals: 6
};
const dateNowSpy = jest
.spyOn(Date, 'now')
.mockImplementation(() => new Date(2021, 5, 16, 8).getTime());
render(<CurrentTokenSale pair={pair} saleTokenInfo={saleTokenInfo} />);
const priceCard = (await screen.findByText('Price')).closest('div');
const coinsRemainingCard = (await screen.findByText('Coins Remaining')).closest('div');
const timeRemainingCard = (await screen.findByText('Time Remaining')).closest('div');
const currentWeightCard = (await screen.findByText('Current Weight')).closest('div');
const aboutCard = (await screen.findByText('About')).closest('div');
// ((500000000000 / 5) / (42000000123456 / 95)) = 2.261904755 * $0.99 = $2.239285714
expect(within(priceCard).getByText('$2.24')).toBeInTheDocument();
expect(within(coinsRemainingCard).getByText('42,000,000.123456')).toBeInTheDocument();
// 2021-06-16 @ 8am -> 2021-06-18 @ 11:10am
expect(within(timeRemainingCard).getByText('2d : 3h : 10m')).toBeInTheDocument();
expect(within(currentWeightCard).getByText('5 : 95')).toBeInTheDocument();
expect(within(aboutCard).getByText('A brand new token for sale!')).toBeInTheDocument();
expect(getWeights).toHaveBeenCalledWith(mockTerraClient, 'terra1', 'uusd');
expect(getPool).toHaveBeenCalledWith(mockTerraClient, 'terra1');
dateNowSpy.mockRestore();
});
});
Example #14
Source File: CreateMappingProfileFormRoute.test.js From ui-data-export with Apache License 2.0 | 4 votes |
describe('CreateMappingProfileFormRoute', () => {
describe('creating new mapping profile', () => {
const onSubmitNavigateMock = jest.fn();
const onSubmitMock = jest.fn();
const sendCalloutMock = jest.fn();
beforeEach(() => {
renderWithIntl(
<CreateMappingProfileFormRouteContainer
allTransformations={allMappingProfilesTransformations}
sendCallout={sendCalloutMock}
onSubmitNavigate={onSubmitNavigateMock}
onSubmit={onSubmitMock}
/>,
translationsProperties
);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should initiate creating of mapping profile with correct values', async () => {
const name = 'New mapping profile';
const submitFormButton = screen.getByRole('button', { name: 'Save & close' });
userEvent.type(screen.getByLabelText('Name*'), name);
userEvent.click(recordTypesHoldings());
userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
const modal = screen.getByRole('document');
const saveTransrormationsButton = within(modal).getByRole('button', { name: 'Save & close' });
const tableRow = screen.getByRole('row', { name: 'Holdings - Call number - Call number' });
const checkbox = within(tableRow).getByRole('checkbox');
const textFields = within(tableRow).getAllByRole('textbox');
userEvent.click(checkbox);
userEvent.type(textFields[0], '500');
userEvent.type(textFields[3], '$a');
userEvent.click(saveTransrormationsButton);
userEvent.click(submitFormButton);
await waitFor(() => {
expect(sendCalloutMock.mock.calls[0][0]).not.toHaveProperty('type');
expect(sendCalloutMock.mock.calls[0][0].message.props.id).toBe('ui-data-export.mappingProfiles.create.successCallout');
expect(onSubmitNavigateMock).toHaveBeenCalled();
});
});
it('should display validation error when name field is empty', () => {
userEvent.click(recordTypesHoldings());
userEvent.click(saveAndCloseBtn());
expect(getByText(document.querySelector('[data-test-mapping-profile-form-name]'), 'Please enter a value')).toBeVisible();
expect(screen.getByLabelText('Name*')).not.toBeValid();
expect(onSubmitMock).not.toBeCalled();
});
it('should initiate displaying of error callout', async () => {
onSubmitMock.mockImplementationOnce(() => Promise.reject());
const submitFormButton = screen.getByRole('button', { name: 'Save & close' });
userEvent.type(screen.getByLabelText('Name*'), 'Name');
userEvent.click(screen.getByRole('checkbox', { name: 'Holdings' }));
userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
const modal = screen.getByRole('document');
const saveTransrormationsButton = within(modal).getByRole('button', { name: 'Save & close' });
const tableRow = screen.getByRole('row', { name: 'Holdings - Call number - Call number' });
const checkbox = within(tableRow).getByRole('checkbox');
const textFields = within(tableRow).getAllByRole('textbox');
userEvent.click(checkbox);
userEvent.type(textFields[0], '500');
userEvent.type(textFields[3], '$a');
userEvent.click(saveTransrormationsButton);
userEvent.click(submitFormButton);
await waitFor(() => {
expect(sendCalloutMock).toBeCalledWith(
expect.objectContaining({ type: 'error' })
);
expect(sendCalloutMock.mock.calls[0][0].message.props.id).toBe('ui-data-export.mappingProfiles.create.errorCallout');
});
});
});
});
Example #15
Source File: JobProfileDetails.test.js From ui-data-export with Apache License 2.0 | 4 votes |
describe('JobProfileDetails', () => {
const stripes = {
connect: Component => props => (
<Component
{... props}
mutator={{}}
resources={{}}
/>
),
};
const renderJobProfileDetails = () => {
renderWithIntl(
<SettingsComponentBuilder>
<JobProfileDetails
stripes={stripes}
jobProfile={jobProfile}
mappingProfile={mappingProfile}
isDefaultProfile
isProfileUsed
onCancel={noop}
onDelete={noop}
/>
</SettingsComponentBuilder>,
translationsProperties
);
};
it('should display job profile details', () => {
renderJobProfileDetails();
const dialog = screen.getByRole('dialog');
expect(dialog).toBeVisible();
const headings = within(dialog).getAllByRole('heading', { name: jobProfile.name });
headings.forEach(heading => expect(heading).toBeVisible());
const summary = within(dialog).getByRole('region', { name: /summary/i });
const labelsAndValues = [
'Record created: 12/4/2018 11:22 AM',
'Record last updated: 12/4/2018 1:28 PM',
commonTranslations.name,
jobProfile.name,
translations.description,
jobProfile.description,
translations.mappingProfile,
mappingProfile.name,
];
labelsAndValues.forEach(el => expect(within(summary).getByText(el)).toBeVisible());
});
it('should display action buttons in the proper state', () => {
renderJobProfileDetails();
const actionButton = screen.getByText('Actions');
userEvent.click(actionButton);
const deleteButton = screen.getByText(commonTranslations.delete);
const duplicateButton = screen.getByText(commonTranslations.duplicate);
const editButton = screen.getByText(commonTranslations.edit);
expect(deleteButton).toBeEnabled();
expect(duplicateButton).toBeEnabled();
expect(editButton).toBeEnabled();
});
describe('rendering details without description for a job profile which is not already in use', () => {
const renderJobProfileWitoutDescription = () => {
renderWithIntl(
<SettingsComponentBuilder>
<JobProfileDetails
stripes={stripes}
jobProfile={{
...jobProfile,
description: null,
}}
mappingProfile={mappingProfile}
isDefaultProfile={false}
isProfileUsed={false}
onCancel={noop}
/>
</SettingsComponentBuilder>,
translationsProperties
);
};
it('should display no value in description', () => {
renderJobProfileWitoutDescription();
const description = document.querySelector('[data-test-job-profile-description]');
expect(within(description).getByText('-')).toBeVisible();
});
it('should display action buttons in the proper state', () => {
renderJobProfileWitoutDescription();
const actionButton = screen.getByText('Actions');
userEvent.click(actionButton);
const deleteButton = screen.getByText(commonTranslations.delete);
const duplicateButton = screen.getByText(commonTranslations.duplicate);
const editButton = screen.getByText(commonTranslations.edit);
expect(deleteButton).toBeEnabled();
expect(duplicateButton).toBeEnabled();
expect(editButton).toBeEnabled();
});
describe('clicking on delete profiles button', () => {
it('should display delete confirmation modal', async () => {
renderJobProfileWitoutDescription();
const actionButton = screen.getByText('Actions');
userEvent.click(actionButton);
const deleteButton = screen.getByText(commonTranslations.delete);
userEvent.click(deleteButton);
const modal = screen.getAllByRole('dialog').find(dialog => within(dialog).getByRole('heading', { name: /delete/i }));
expect(modal).toBeVisible();
userEvent.click(within(modal).getByRole('button', { name: /cancel/i }));
await waitForElementToBeRemoved(modal);
});
});
});
describe('rendering job profile details in loading state', () => {
const renderJobProfileWithLoading = () => {
renderWithIntl(
<SettingsComponentBuilder>
<JobProfileDetails
stripes={stripes}
isLoading
isDefaultProfile={false}
isProfileUsed
onCancel={noop}
/>
</SettingsComponentBuilder>,
translationsProperties
);
};
it('should display preloader', () => {
renderJobProfileWithLoading();
expect(document.querySelector('[data-test-preloader]')).toBeVisible();
});
});
});
Example #16
Source File: JobProfileDetailsRoute.test.js From ui-data-export with Apache License 2.0 | 4 votes |
describe('JobProfileDetails', () => {
let server;
beforeEach(() => {
server = new Pretender();
});
afterEach(() => {
server.shutdown();
});
describe('rendering details for a job profile without job profile data', () => {
it('should display preloader', async () => {
server.get('/data-export/mapping-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(mappingProfile),
]);
setupJobProfileDetailsRoute();
const dialog = screen.getByRole('dialog');
expect(dialog).toBeVisible();
expect(document.querySelector('[data-test-preloader]')).toBeVisible();
});
it('should history includes location.search', () => {
server.get('/data-export/mapping-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(mappingProfile),
]);
setupJobProfileDetailsRoute();
const cancelButton = screen.getByRole('button', { name: /cancel/i });
expect(cancelButton).toBeEnabled();
userEvent.click(cancelButton);
expect(history.some(el => el.includes('?location'))).toBeTruthy();
});
});
describe('rendering details for a job profile without mapping profile data', () => {
it('should display preloader', async () => {
server.get('/data-export/job-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(jobProfile),
]);
setupJobProfileDetailsRoute({ matchParams: { id: JOB_PROFILE_ID } });
expect(document.querySelector('[data-test-preloader]')).toBeVisible();
});
});
describe('rendering details for a job with mapping profile', () => {
it('should display preloader for default job profile', async () => {
server.get('/data-export/job-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(jobProfile),
]);
server.get('/data-export/mapping-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(mappingProfile),
]);
setupJobProfileDetailsRoute({ matchParams: { id: JOB_PROFILE_ID } });
expect(document.querySelector('[data-test-preloader]')).toBeVisible();
});
});
describe('rendering details for non default job profile without job execution data', () => {
const nonDefaultJobProfileId = 'job-profile-id';
it('should display job profile details for non default job profile', async () => {
server.get('/data-export/job-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify({
...jobProfile,
id: nonDefaultJobProfileId,
}),
]);
server.get('/data-export/mapping-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(mappingProfile),
]);
setupJobProfileDetailsRoute({ matchParams: { id: nonDefaultJobProfileId } });
const summary = await screen.findByRole('region', { name: /summary/i });
const labelsAndValues = [
'Record created: 12/4/2018 11:22 AM',
'Record last updated: 12/4/2018 1:28 PM',
jobProfile.name,
jobProfile.description,
mappingProfile.name,
];
labelsAndValues.forEach(el => expect(within(summary).getByText(el)).toBeVisible());
});
});
describe('rendering details for default job profile with job execution data', () => {
it('should display job profile details', async () => {
server.get('/data-export/job-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(jobProfile),
]);
server.get('/data-export/mapping-profiles/:id', () => [
200,
{ 'content-type': 'application/json' },
JSON.stringify(mappingProfile),
]);
setupJobProfileDetailsRoute();
const summary = await screen.findByRole('region', { name: /summary/i });
const dialog = screen.getByRole('dialog');
const headings = within(dialog).getAllByRole('heading', { name: jobProfile.name });
headings.forEach(heading => expect(heading).toBeVisible());
const labelsAndValues = [
'Record created: 12/4/2018 11:22 AM',
'Record last updated: 12/4/2018 1:28 PM',
jobProfile.name,
jobProfile.description,
mappingProfile.name,
];
labelsAndValues.forEach(el => expect(within(summary).getByText(el)).toBeVisible());
});
});
});
Example #17
Source File: Tabs-test.js From Lambda with MIT License | 4 votes |
describe('<Tabs />', () => {
beforeEach(() => resetIdCounter());
beforeAll(() => {
// eslint-disable-next-line no-console
console.error = (error, ...args) => {
if (args.length > 0 && typeof error === 'string') {
if (error.endsWith('%s%s')) {
throw new Error(format(error.slice(0, -2), ...args.slice(0, -1)));
}
throw new Error(format(error, ...args));
}
throw new Error(error);
};
});
describe('props', () => {
test('should have sane defaults', () => {
expectToMatchSnapshot(createTabs());
});
test('should honor positive defaultIndex prop', () => {
expectToMatchSnapshot(createTabs({ defaultIndex: 1 }));
});
test('should honor negative defaultIndex prop', () => {
expectToMatchSnapshot(createTabs({ defaultIndex: -1 }));
});
test('should call onSelect when selection changes', () => {
const called = { index: -1, last: -1 };
render(
createTabs({
onSelect(index, last) {
called.index = index;
called.last = last;
},
}),
);
userEvent.click(screen.getByTestId('tab2'));
expect(called.index).toBe(1);
expect(called.last).toBe(0);
});
test('should accept className', () => {
expectToMatchSnapshot(createTabs({ className: 'foobar' }));
});
test('should accept domRef', () => {
let domNode;
render(
createTabs({
domRef: (node) => {
domNode = node;
},
}),
);
expect(domNode).not.toBeUndefined();
expect(domNode.className).toBe('react-tabs');
});
});
describe('child props', () => {
test('should reset ids correctly', () => {
expectToMatchSnapshot(createTabs());
resetIdCounter();
expectToMatchSnapshot(createTabs());
});
});
describe('interaction', () => {
describe('mouse', () => {
test('should update selectedIndex when clicked', () => {
render(createTabs());
userEvent.click(screen.getByTestId('tab2'));
assertTabSelected(2);
});
test('should update selectedIndex when tab child is clicked', () => {
render(createTabs());
userEvent.click(screen.getByTestId('tab3'));
assertTabSelected(3);
});
test('should not change selectedIndex when clicking a disabled tab', () => {
render(createTabs({ defaultIndex: 0 }));
userEvent.click(screen.getByTestId('tab4'));
assertTabSelected(1);
});
});
describe('keyboard', () => {
test('should update selectedIndex when arrow right key pressed', () => {
render(createTabs());
const element = screen.getByTestId('tab1');
userEvent.click(element);
userEvent.type(element, '{arrowright}');
assertTabSelected(2);
});
test('should update selectedIndex when arrow left key pressed (RTL)', () => {
render(createTabs({ direction: 'rtl' }));
const element = screen.getByTestId('tab1');
userEvent.click(element);
userEvent.type(element, '{arrowleft}');
assertTabSelected(2);
});
test.skip('should not change selectedIndex when arrow left key pressed on a disabled tab', () => {
render(createTabs());
const element = screen.getByTestId('tab4');
userEvent.click(element);
userEvent.type(element, '{arrowleft}');
assertTabSelected(1);
});
});
});
describe('performance', () => {
test('should only render the selected tab panel', () => {
render(createTabs());
const tabPanels = screen.getAllByRole('tabpanel');
expect(tabPanels[0]).toHaveTextContent('Hello Tab1');
expect(tabPanels[1]).toHaveTextContent('');
expect(tabPanels[2]).toHaveTextContent('');
expect(tabPanels[3]).toHaveTextContent('');
userEvent.click(screen.getByTestId('tab2'));
expect(tabPanels[0]).toHaveTextContent('');
expect(tabPanels[1]).toHaveTextContent('Hello Tab2');
expect(tabPanels[2]).toHaveTextContent('');
expect(tabPanels[3]).toHaveTextContent('');
userEvent.click(screen.getByTestId('tab3'));
expect(tabPanels[0]).toHaveTextContent('');
expect(tabPanels[1]).toHaveTextContent('');
expect(tabPanels[2]).toHaveTextContent('Hello Tab3');
expect(tabPanels[3]).toHaveTextContent('');
});
test('should render all tabs if forceRenderTabPanel is true', () => {
expectToMatchSnapshot(createTabs({ forceRenderTabPanel: true }));
});
});
describe('validation', () => {
test('should result with warning when tabs/panels are imbalanced', () => {
expect(() =>
render(
<Tabs>
<TabList>
<Tab>Foo</Tab>
</TabList>
</Tabs>,
),
).toThrowErrorMatchingSnapshot();
});
test('should result with warning when tab outside of tablist', () => {
expect(() =>
render(
<Tabs>
<TabList>
<Tab>Foo</Tab>
</TabList>
<Tab>Foo</Tab>
<TabPanel />
<TabPanel />
</Tabs>,
),
).toThrowErrorMatchingSnapshot();
});
test('should result with warning when multiple tablist components exist', () => {
expect(() =>
render(
<Tabs>
<TabList>
<Tab>Foo</Tab>
</TabList>
<TabList>
<Tab>Foo</Tab>
</TabList>
<TabPanel />
<TabPanel />
</Tabs>,
),
).toThrowErrorMatchingSnapshot();
});
test('should result with warning when onSelect missing when selectedIndex set', () => {
expect(() =>
render(
<Tabs selectedIndex={1}>
<TabList>
<Tab>Foo</Tab>
</TabList>
<TabPanel>Foo</TabPanel>
</Tabs>,
),
).toThrowErrorMatchingSnapshot();
});
test('should result with warning when defaultIndex and selectedIndex set', () => {
expect(() =>
render(
<Tabs selectedIndex={1} defaultIndex={1}>
<TabList>
<Tab>Foo</Tab>
</TabList>
<TabPanel>Foo</TabPanel>
</Tabs>,
),
).toThrowErrorMatchingSnapshot();
});
test('should result with warning when tabs/panels are imbalanced and it should ignore non tab children', () => {
expect(() =>
render(
<Tabs>
<TabList>
<Tab>Foo</Tab>
<div>+</div>
</TabList>
<TabPanel>Hello Foo</TabPanel>
<TabPanel>Hello Bar</TabPanel>
</Tabs>,
),
).toThrowErrorMatchingSnapshot();
});
test('should allow random order for elements', () => {
expectToMatchSnapshot(
<Tabs forceRenderTabPanel>
<TabPanel>Hello Foo</TabPanel>
<TabList>
<Tab>Foo</Tab>
<Tab>Bar</Tab>
</TabList>
<TabPanel>Hello Bar</TabPanel>
</Tabs>,
);
});
test('should not throw a warning when wrong element is found', () => {
expectToMatchSnapshot(
<Tabs>
<TabList>
<Tab />
<div />
</TabList>
<TabPanel />
</Tabs>,
);
});
test('should be okay with rendering without any children', () => {
expectToMatchSnapshot(<Tabs />);
});
test('should be okay with rendering just TabList', () => {
expectToMatchSnapshot(
<Tabs>
<TabList />
</Tabs>,
);
});
test('should gracefully render null', () => {
expectToMatchSnapshot(
<Tabs>
<TabList>
<Tab>Tab A</Tab>
{false && <Tab>Tab B</Tab>}
</TabList>
<TabPanel>Content A</TabPanel>
{false && <TabPanel>Content B</TabPanel>}
</Tabs>,
);
});
test('should support nested tabs', () => {
render(
<Tabs data-testid="first">
<TabList>
<Tab data-testid="tab1" />
<Tab />
</TabList>
<TabPanel data-testid="panel1">
Hello Tab1
<Tabs data-testid="second">
<TabList>
<Tab />
<Tab data-testid="tab2" />
</TabList>
<TabPanel />
<TabPanel data-testid="panel2">Hello Tab2</TabPanel>
</Tabs>
</TabPanel>
<TabPanel />
</Tabs>,
);
userEvent.click(within(screen.getByTestId('second')).getByTestId('tab2'));
assertTabSelected(1);
assertTabSelected(2, within(screen.getByTestId('second')));
});
test('should allow other DOM nodes', () => {
expectToMatchSnapshot(
<Tabs>
<div id="tabs-nav-wrapper">
<button type="button">Left</button>
<div className="tabs-container">
<TabList>
<Tab />
<Tab />
</TabList>
</div>
<button type="button">Right</button>
</div>
<div className="tab-panels">
<TabPanel />
<TabPanel />
</div>
</Tabs>,
);
});
});
test('should pass through custom properties', () => {
expectToMatchSnapshot(<Tabs data-tooltip="Tooltip contents" />);
});
test('should not add known props to dom', () => {
expectToMatchSnapshot(<Tabs defaultIndex={3} />);
});
test('should cancel if event handler returns false', () => {
render(createTabs({ onSelect: () => false }));
assertTabSelected(1);
userEvent.click(screen.getByTestId('tab2'));
assertTabSelected(1);
userEvent.click(screen.getByTestId('tab3'));
assertTabSelected(1);
});
test('should trigger onSelect handler when clicking', () => {
let wasClicked = false;
render(
createTabs({
onSelect: () => {
wasClicked = true;
},
}),
);
assertTabSelected(1);
userEvent.click(screen.getByTestId('tab2'));
assertTabSelected(2);
expect(wasClicked).toBe(true);
});
test('should trigger onSelect handler when clicking on open tab', () => {
let wasClicked = false;
render(
createTabs({
onSelect: () => {
wasClicked = true;
},
}),
);
assertTabSelected(1);
userEvent.click(screen.getByTestId('tab1'));
assertTabSelected(1);
expect(wasClicked).toBe(true);
});
test('should switch tabs if setState is called within onSelect', () => {
class Wrap extends React.Component {
state = {};
handleSelect = () => this.setState({ foo: 'bar' });
render() {
const { foo } = this.state;
return createTabs({
onSelect: this.handleSelect,
className: foo,
});
}
}
render(<Wrap />);
userEvent.click(screen.getByTestId('tab2'));
assertTabSelected(2);
userEvent.click(screen.getByTestId('tab3'));
assertTabSelected(3);
});
test('should allow for higher order components', () => {
expectToMatchSnapshot(
<Tabs>
<TabListWrapper>
<TabWrapper>Foo</TabWrapper>
<TabWrapper>Bar</TabWrapper>
</TabListWrapper>
<TabPanelWrapper>Foo</TabPanelWrapper>
<TabPanelWrapper>Bar</TabPanelWrapper>
</Tabs>,
);
});
test('should allow string children', () => {
expectToMatchSnapshot(
<Tabs>
Foo
<TabList>
Foo
<Tab>Foo</Tab>
Foo
<Tab>Bar</Tab>
Foo
</TabList>
<TabPanel>Bar</TabPanel>
<TabPanel>Foo</TabPanel>
Foo
</Tabs>,
);
});
});
Example #18
Source File: MappingProfileDetails.test.js From ui-data-export with Apache License 2.0 | 4 votes |
describe('MappingProfileDetails', () => {
describe('rendering details for a mapping profile which is already in use', () => {
const renderMappingProfileDetails = () => renderWithIntl(
<MappingProfileDetailsContainer
allTransformations={allMappingProfilesTransformations}
isDefaultProfile
isProfileUsed
/>,
translationsProperties
);
it('should display mapping profile details', () => {
renderMappingProfileDetails();
const dialog = screen.getByRole('dialog');
expect(dialog).toBeVisible();
const headings = within(dialog).getAllByRole('heading', { name: mappingProfileWithTransformations.name });
headings.forEach(heading => expect(heading).toBeVisible());
expect(within(dialog).getByRole('button', { name: /collapse all/i })).toBeVisible();
const summary = within(dialog).getByRole('region', { name: /summary/i });
const transformations = within(dialog).getByRole('region', { name: /transformations/i });
expect(summary).toBeVisible();
expect(transformations).toBeVisible();
const labelsAndValues = [
'Record created: 12/4/2018 1:29 AM',
'Record last updated: 12/4/2018 1:29 AM',
commonTranslations.name,
'AP Holdings 1',
translations.description,
'AP Holdings 1 description',
translations.outputFormat,
'MARC',
commonTranslations.folioRecordType,
commonTranslations['recordTypes.holdings'],
];
labelsAndValues.forEach(el => expect(within(summary).getByText(el)).toBeVisible());
});
it('should display correct transformations fields headers', () => {
renderMappingProfileDetails();
expect(screen.getByRole('columnheader', { name: translations['mappingProfiles.transformations.fieldName'] })).toBeVisible();
expect(screen.getByRole('columnheader', { name: translations['mappingProfiles.transformations.transformation'] })).toBeVisible();
});
it('should display correct transformations values', () => {
renderMappingProfileDetails();
const transformationListRows = getAllByRole(screen.getByRole('rowgroup'), 'row');
expect(getByText(transformationListRows[0], 'Holdings - Call number - Call number')).toBeVisible();
expect(getByText(transformationListRows[0], '11100$a')).toBeVisible();
expect(getByText(transformationListRows[1], 'Holdings - Notes - Action note')).toBeVisible();
expect(getByText(transformationListRows[1], '123 1$12')).toBeVisible();
});
it('should display action buttons in the proper state', () => {
const { container } = renderMappingProfileDetails();
const actionButton = container.querySelector('[data-test-pane-header-actions-button]');
const disableButton = container.querySelector('[data-test-delete-profile-button]');
const duplicateButton = container.querySelector('[data-test-duplicate-profile-button]');
const deleteButton = container.querySelector('[data-test-delete-profile-button]');
userEvent.click(actionButton);
expect(disableButton).toBeDisabled();
expect(duplicateButton).toBeEnabled();
expect(deleteButton).toBeDisabled();
});
});
describe('rendering details for a mapping profile which is already in use', () => {
const renderMappingProfileDetailsWithMapping = () => renderWithIntl(
<MappingProfileDetailsContainer mappingProfile={mappingProfileWithoutTransformations} />,
translationsProperties
);
it('should display no value in description', () => {
const { container } = renderMappingProfileDetailsWithMapping();
const summaryDescriptionValue = container.querySelector('[data-test-mapping-profile-description]');
expect(getByText(summaryDescriptionValue, '-')).toBeVisible();
});
it('should not display delete confirmation modal', () => {
const { container } = renderMappingProfileDetailsWithMapping();
const deleteConfirmationModal = container.querySelector('#delete-mapping-profile-confirmation-modal');
expect(deleteConfirmationModal).not.toBeInTheDocument();
});
it('should not display transformation list', () => {
const { container } = renderMappingProfileDetailsWithMapping();
const transformationList = container.querySelector('#mapping-profile-transformations-list');
expect(transformationList).not.toBeInTheDocument();
});
it('should display action buttons enabled', () => {
const { container } = renderMappingProfileDetailsWithMapping();
const actionButton = container.querySelector('[data-test-pane-header-actions-button]');
const disableButton = container.querySelector('[data-test-delete-profile-button]');
const duplicateButton = container.querySelector('[data-test-duplicate-profile-button]');
const deleteButton = container.querySelector('[data-test-delete-profile-button]');
userEvent.click(actionButton);
expect(disableButton).toBeEnabled();
expect(duplicateButton).toBeEnabled();
expect(deleteButton).toBeEnabled();
});
describe('clicking on delete profiles button', () => {
it('should display delete confirmation modal', async () => {
renderMappingProfileDetailsWithMapping();
const actionButton = document.querySelector('[data-test-pane-header-actions-button]');
userEvent.click(actionButton);
const deleteButton = document.querySelector('[data-test-delete-profile-button]');
userEvent.click(deleteButton);
const modal = screen.getAllByRole('dialog').find(dialog => within(dialog).getByRole('heading', { name: /delete/i }));
expect(modal).toBeVisible();
userEvent.click(within(modal).getByRole('button', { name: /cancel/i }));
await waitForElementToBeRemoved(modal);
});
});
});
describe('rendering mapping profile details in loading state', () => {
it('should display preloader', () => {
const { container } = renderWithIntl(<MappingProfileDetailsContainer
mappingProfile={mappingProfileWithoutTransformations}
isLoading
isProfileUsed
/>,
translationsProperties);
expect(container.querySelector('[data-test-preloader]')).toBeVisible();
});
});
});
Example #19
Source File: CommentsView.test.jsx From frontend-app-discussions with GNU Affero General Public License v3.0 | 4 votes |
describe('CommentsView', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
Factory.resetAll();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(threadsApiUrl)
.reply(200, Factory.build('threadsResult'));
axiosMock.onPatch(new RegExp(`${commentsApiUrl}*`)).reply(({
url,
data,
}) => {
const commentId = url.match(/comments\/(?<id>[a-z1-9-]+)\//).groups.id;
const {
rawBody,
} = camelCaseObject(JSON.parse(data));
return [200, Factory.build('comment', {
id: commentId,
rendered_body: rawBody,
raw_body: rawBody,
})];
});
axiosMock.onPost(commentsApiUrl)
.reply(({ data }) => {
const {
rawBody,
threadId,
} = camelCaseObject(JSON.parse(data));
return [200, Factory.build(
'comment',
{
rendered_body: rawBody,
raw_body: rawBody,
thread_id: threadId,
},
)];
});
await executeThunk(fetchThreads(courseId), store.dispatch, store.getState);
mockAxiosReturnPagedComments();
mockAxiosReturnPagedCommentsResponses();
});
describe('for all post types', () => {
function assertLastUpdateData(data) {
expect(JSON.parse(axiosMock.history.patch[axiosMock.history.patch.length - 1].data)).toMatchObject(data);
}
it('should show and hide the editor', async () => {
renderComponent(discussionPostId);
await waitFor(() => screen.findByText('comment number 1', { exact: false }));
await act(async () => {
fireEvent.click(
screen.getByRole('button', { name: /add a response/i }),
);
});
expect(screen.queryByTestId('tinymce-editor')).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
});
expect(screen.queryByTestId('tinymce-editor')).not.toBeInTheDocument();
});
it('should allow posting a response', async () => {
renderComponent(discussionPostId);
await waitFor(() => screen.findByText('comment number 1', { exact: false }));
await act(async () => {
fireEvent.click(
screen.getByRole('button', { name: /add a response/i }),
);
});
act(() => {
fireEvent.change(screen.getByTestId('tinymce-editor'), { target: { value: 'testing123' } });
});
await act(async () => {
fireEvent.click(
screen.getByText(/submit/i),
);
});
expect(screen.queryByTestId('tinymce-editor')).not.toBeInTheDocument();
await waitFor(async () => expect(await screen.findByText('testing123', { exact: false })).toBeInTheDocument());
});
it('should allow posting a comment', async () => {
renderComponent(discussionPostId);
await waitFor(() => screen.findByText('comment number 1', { exact: false }));
await act(async () => {
fireEvent.click(
screen.getAllByRole('button', { name: /add a comment/i })[0],
);
});
act(() => {
fireEvent.change(screen.getByTestId('tinymce-editor'), { target: { value: 'testing123' } });
});
await act(async () => {
fireEvent.click(
screen.getByText(/submit/i),
);
});
expect(screen.queryByTestId('tinymce-editor')).not.toBeInTheDocument();
await waitFor(async () => expect(await screen.findByText('testing123', { exact: false })).toBeInTheDocument());
});
it('should allow editing an existing comment', async () => {
renderComponent(discussionPostId);
await waitFor(() => screen.findByText('comment number 1', { exact: false }));
await act(async () => {
fireEvent.click(
// The first edit menu is for the post, the second will be for the first comment.
screen.getAllByRole('button', { name: /actions menu/i })[1],
);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /edit/i }));
});
act(() => {
fireEvent.change(screen.getByTestId('tinymce-editor'), { target: { value: 'testing123' } });
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /submit/i }));
});
await waitFor(async () => {
expect(await screen.findByText('testing123', { exact: false })).toBeInTheDocument();
});
});
async function setupCourseConfig(reasonCodesEnabled = true) {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`).reply(200, {
user_is_privileged: true,
reason_codes_enabled: reasonCodesEnabled,
editReasons: [
{ code: 'reason-1', label: 'reason 1' },
{ code: 'reason-2', label: 'reason 2' },
],
postCloseReasons: [
{ code: 'reason-1', label: 'reason 1' },
{ code: 'reason-2', label: 'reason 2' },
],
});
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/settings`).reply(200, {});
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
}
it('should show reason codes when editing an existing comment', async () => {
setupCourseConfig();
renderComponent(discussionPostId);
await waitFor(() => screen.findByText('comment number 1', { exact: false }));
await act(async () => {
fireEvent.click(
// The first edit menu is for the post, the second will be for the first comment.
screen.getAllByRole('button', { name: /actions menu/i })[1],
);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /edit/i }));
});
expect(screen.queryByRole('combobox', { name: /reason for editing/i })).toBeInTheDocument();
expect(screen.getAllByRole('option', { name: /reason \d/i })).toHaveLength(2);
await act(async () => {
fireEvent.change(screen.queryByRole('combobox', { name: /reason for editing/i }), { target: { value: null } });
});
await act(async () => {
fireEvent.change(screen.queryByRole('combobox', { name: /reason for editing/i }), { target: { value: 'reason-1' } });
});
await act(async () => {
fireEvent.change(screen.getByTestId('tinymce-editor'), { target: { value: 'testing123' } });
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /submit/i }));
});
assertLastUpdateData({ edit_reason_code: 'reason-1' });
});
it('should show reason codes when closing a post', async () => {
setupCourseConfig();
renderComponent(discussionPostId);
await act(async () => {
fireEvent.click(
// The first edit menu is for the post
screen.getAllByRole('button', {
name: /actions menu/i,
})[0],
);
});
expect(screen.queryByRole('dialog', { name: /close post/i })).not.toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /close/i }));
});
expect(screen.queryByRole('dialog', { name: /close post/i })).toBeInTheDocument();
expect(screen.queryByRole('combobox', { name: /reason/i })).toBeInTheDocument();
expect(screen.getAllByRole('option', { name: /reason \d/i })).toHaveLength(2);
await act(async () => {
fireEvent.change(screen.queryByRole('combobox', { name: /reason/i }), { target: { value: 'reason-1' } });
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /close post/i }));
});
expect(screen.queryByRole('dialog', { name: /close post/i })).not.toBeInTheDocument();
assertLastUpdateData({ closed: true, close_reason_code: 'reason-1' });
});
it('should close the post directly if reason codes are not enabled', async () => {
setupCourseConfig(false);
renderComponent(discussionPostId);
await act(async () => {
fireEvent.click(
// The first edit menu is for the post
screen.getAllByRole('button', { name: /actions menu/i })[0],
);
});
expect(screen.queryByRole('dialog', { name: /close post/i })).not.toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /close/i }));
});
expect(screen.queryByRole('dialog', { name: /close post/i })).not.toBeInTheDocument();
assertLastUpdateData({ closed: true });
});
it.each([true, false])(
'should reopen the post directly when reason codes enabled=%s',
async (reasonCodesEnabled) => {
setupCourseConfig(reasonCodesEnabled);
renderComponent(closedPostId);
await act(async () => {
fireEvent.click(
// The first edit menu is for the post
screen.getAllByRole('button', { name: /actions menu/i })[0],
);
});
expect(screen.queryByRole('dialog', { name: /close post/i })).not.toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /reopen/i }));
});
expect(screen.queryByRole('dialog', { name: /close post/i })).not.toBeInTheDocument();
assertLastUpdateData({ closed: false });
},
);
it('should show the editor if the post is edited', async () => {
setupCourseConfig(false);
renderComponent(discussionPostId);
await act(async () => {
fireEvent.click(
// The first edit menu is for the post
screen.getAllByRole('button', { name: /actions menu/i })[0],
);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /edit/i }));
});
expect(testLocation.pathname).toBe(`/${courseId}/posts/${discussionPostId}/edit`);
});
it('should allow pinning the post', async () => {
renderComponent(discussionPostId);
await act(async () => {
fireEvent.click(
// The first edit menu is for the post
screen.getAllByRole('button', { name: /actions menu/i })[0],
);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /pin/i }));
});
assertLastUpdateData({ pinned: false });
});
it('should allow reporting the post', async () => {
renderComponent(discussionPostId);
await act(async () => {
fireEvent.click(
// The first edit menu is for the post
screen.getAllByRole('button', { name: /actions menu/i })[0],
);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /report/i }));
});
assertLastUpdateData({ abuse_flagged: true });
});
it('handles liking a comment', async () => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
const view = screen.getByTestId('comment-comment-1');
const likeButton = within(view).getByRole('button', { name: /like/i });
await act(async () => {
fireEvent.click(likeButton);
});
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ voted: true });
});
it.each([
['endorsing comments', 'Endorse', { endorsed: true }],
['reporting comments', 'Report', { abuse_flagged: true }],
])('handles %s', async (label, buttonLabel, patchData) => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
// There should be three buttons, one for the post, the second for the
// comment and the third for a response to that comment
const actionButtons = screen.queryAllByRole('button', { name: /actions menu/i });
await act(async () => {
fireEvent.click(actionButtons[1]);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: buttonLabel }));
});
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject(patchData);
});
});
describe('for discussion thread', () => {
const findLoadMoreCommentsButton = () => screen.findByTestId('load-more-comments');
it('shown spinner when post isn\'t loaded', async () => {
renderComponent('unloaded-id');
expect(await screen.findByTestId('loading-indicator'))
.toBeInTheDocument();
});
it('initially loads only the first page', async () => {
renderComponent(discussionPostId);
expect(await screen.findByText('comment number 1', { exact: false }))
.toBeInTheDocument();
expect(screen.queryByText('comment number 2', { exact: false }))
.not
.toBeInTheDocument();
});
it('pressing load more button will load next page of comments', async () => {
renderComponent(discussionPostId);
const loadMoreButton = await findLoadMoreCommentsButton();
fireEvent.click(loadMoreButton);
await screen.findByText('comment number 1', { exact: false });
await screen.findByText('comment number 2', { exact: false });
});
it('newly loaded comments are appended to the old ones', async () => {
renderComponent(discussionPostId);
const loadMoreButton = await findLoadMoreCommentsButton();
fireEvent.click(loadMoreButton);
await screen.findByText('comment number 1', { exact: false });
// check that comments from the first page are also displayed
expect(screen.queryByText('comment number 2', { exact: false }))
.toBeInTheDocument();
});
it('load more button is hidden when no more comments pages to load', async () => {
const totalPages = 2;
renderComponent(discussionPostId);
const loadMoreButton = await findLoadMoreCommentsButton();
for (let page = 1; page < totalPages; page++) {
fireEvent.click(loadMoreButton);
}
await screen.findByText('comment number 2', { exact: false });
await expect(findLoadMoreCommentsButton())
.rejects
.toThrow();
});
});
describe('for question thread', () => {
const findLoadMoreCommentsButtons = () => screen.findAllByTestId('load-more-comments');
it('initially loads only the first page', async () => {
act(() => renderComponent(questionPostId));
expect(await screen.findByText('comment number 3', { exact: false }))
.toBeInTheDocument();
expect(await screen.findByText('endorsed comment number 5', { exact: false }))
.toBeInTheDocument();
expect(screen.queryByText('comment number 4', { exact: false }))
.not
.toBeInTheDocument();
});
it('pressing load more button will load next page of comments', async () => {
act(() => {
renderComponent(questionPostId);
});
const [loadMoreButtonEndorsed, loadMoreButtonUnendorsed] = await findLoadMoreCommentsButtons();
// Both load more buttons should show
expect(await findLoadMoreCommentsButtons()).toHaveLength(2);
expect(await screen.findByText('unendorsed comment number 3', { exact: false }))
.toBeInTheDocument();
expect(await screen.findByText('endorsed comment number 5', { exact: false }))
.toBeInTheDocument();
// Comments from next page should not be loaded yet.
expect(await screen.queryByText('endorsed comment number 6', { exact: false }))
.not
.toBeInTheDocument();
expect(await screen.queryByText('unendorsed comment number 4', { exact: false }))
.not
.toBeInTheDocument();
await act(async () => {
fireEvent.click(loadMoreButtonEndorsed);
});
// Endorsed comment from next page should be loaded now.
await waitFor(() => expect(screen.queryByText('endorsed comment number 6', { exact: false }))
.toBeInTheDocument());
// Unendorsed comment from next page should not be loaded yet.
expect(await screen.queryByText('unendorsed comment number 4', { exact: false }))
.not
.toBeInTheDocument();
// Now only one load more buttons should show, for unendorsed comments
expect(await findLoadMoreCommentsButtons()).toHaveLength(1);
await act(async () => {
fireEvent.click(loadMoreButtonUnendorsed);
});
// Unendorsed comment from next page should be loaded now.
await waitFor(() => expect(screen.queryByText('unendorsed comment number 4', { exact: false }))
.toBeInTheDocument());
await expect(findLoadMoreCommentsButtons()).rejects.toThrow();
});
});
describe('comments responses', () => {
const findLoadMoreCommentsResponsesButton = () => screen.findByTestId('load-more-comments-responses');
it('initially loads only the first page', async () => {
renderComponent(discussionPostId);
await waitFor(() => screen.findByText('comment number 7', { exact: false }));
expect(screen.queryByText('comment number 8', { exact: false })).not.toBeInTheDocument();
});
it('pressing load more button will load next page of responses', async () => {
renderComponent(discussionPostId);
const loadMoreButton = await findLoadMoreCommentsResponsesButton();
await act(async () => {
fireEvent.click(loadMoreButton);
});
await screen.findByText('comment number 8', { exact: false });
});
it('newly loaded responses are appended to the old ones', async () => {
renderComponent(discussionPostId);
const loadMoreButton = await findLoadMoreCommentsResponsesButton();
await act(async () => {
fireEvent.click(loadMoreButton);
});
await screen.findByText('comment number 8', { exact: false });
// check that comments from the first page are also displayed
expect(screen.queryByText('comment number 7', { exact: false })).toBeInTheDocument();
});
it('load more button is hidden when no more responses pages to load', async () => {
const totalPages = 2;
renderComponent(discussionPostId);
const loadMoreButton = await findLoadMoreCommentsResponsesButton();
for (let page = 1; page < totalPages; page++) {
act(() => {
fireEvent.click(loadMoreButton);
});
}
await screen.findByText('comment number 8', { exact: false });
await expect(findLoadMoreCommentsResponsesButton())
.rejects
.toThrow();
});
it('handles liking a comment', async () => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
const view = screen.getByTestId('comment-comment-1');
const likeButton = within(view).getByRole('button', { name: /like/i });
await act(async () => {
fireEvent.click(likeButton);
});
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ voted: true });
});
it.each([
['endorsing comments', 'Endorse', { endorsed: true }],
['reporting comments', 'Report', { abuse_flagged: true }],
])('handles %s', async (label, buttonLabel, patchData) => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
// There should be three buttons, one for the post, the second for the
// comment and the third for a response to that comment
const actionButtons = screen.queryAllByRole('button', { name: /actions menu/i });
await act(async () => {
fireEvent.click(actionButtons[1]);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: buttonLabel }));
});
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject(patchData);
});
});
describe.each([
{ component: 'post', testId: 'post-thread-1' },
{ component: 'comment', testId: 'comment-comment-1' },
{ component: 'reply', testId: 'reply-comment-7' },
])('delete confirmation modal', ({
component,
testId,
}) => {
test(`for ${component}`, async () => {
renderComponent(discussionPostId);
// Wait for the content to load
await waitFor(() => expect(screen.queryByText('comment number 7', { exact: false })).toBeInTheDocument());
const content = screen.getByTestId(testId);
const actionsButton = within(content).getAllByRole('button', { name: /actions menu/i })[0];
await act(async () => {
fireEvent.click(actionsButton);
});
expect(screen.queryByRole('dialog', { name: /delete \w+/i, exact: false })).not.toBeInTheDocument();
const deleteButton = within(content).queryByRole('button', { name: /delete/i });
await act(async () => {
fireEvent.click(deleteButton);
});
expect(screen.queryByRole('dialog', { name: /delete \w+/i, exact: false })).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.queryByRole('button', { name: /delete/i }));
});
expect(screen.queryByRole('dialog', { name: /delete \w+/i, exact: false })).not.toBeInTheDocument();
});
});
});
Example #20
Source File: TopicsView.test.jsx From frontend-app-discussions with GNU Affero General Public License v3.0 | 4 votes |
describe('TopicsView', () => {
describe.each(['legacy', 'openedx'])('%s provider', (provider) => {
let inContextTopics;
let globalTopics;
let categories;
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore({
config: { provider },
blocks: {
topics: {},
},
});
Factory.resetAll();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
lastLocation = undefined;
});
async function setupMockResponse() {
if (provider === 'legacy') {
axiosMock
.onGet(topicsApiUrl)
.reply(200, {
courseware_topics: Factory.buildList('category', 2),
non_courseware_topics: Factory.buildList('topic', 3, {}, { topicPrefix: 'ncw' }),
});
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
const state = store.getState();
categories = state.topics.categoryIds;
globalTopics = selectNonCoursewareTopics(state);
inContextTopics = selectCoursewareTopics(state);
} else {
const blocksAPIResponse = getBlocksAPIResponse(true);
const ids = Object.values(blocksAPIResponse.blocks).filter(block => block.type === 'vertical')
.map(block => block.block_id);
const deletedIds = [
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@deleted-vertical-1',
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@deleted-vertical-2',
];
const data = [
...Factory.buildList('topic.v2', 2, { usage_key: null }, { topicPrefix: 'ncw' }),
...ids.map(id => Factory.build('topic.v2', { id })),
...deletedIds.map(id => Factory.build('topic.v2', { id, enabled_in_context: false }, { topicPrefix: 'archived ' })),
];
axiosMock
.onGet(topicsv2ApiUrl)
.reply(200, data);
axiosMock.onGet(blocksAPIURL)
.reply(200, getBlocksAPIResponse(true));
axiosMock.onAny().networkError();
await executeThunk(fetchCourseBlocks(courseId, 'abc123'), store.dispatch, store.getState);
await executeThunk(fetchCourseTopics(courseId), store.dispatch, store.getState);
const state = store.getState();
categories = selectSequences(state);
globalTopics = selectNonCoursewareTopics(state);
inContextTopics = selectCoursewareTopics(state);
}
}
it('displays non-courseware topics', async () => {
await setupMockResponse();
renderComponent();
globalTopics.forEach(topic => {
expect(screen.queryByText(topic.name)).toBeInTheDocument();
});
});
it('displays non-courseware outside of a topic group', async () => {
await setupMockResponse();
renderComponent();
categories.forEach(category => {
// For the new provider categories are blocks so use the display name
// otherwise use the category itself which is a string
expect(screen.queryByText(category.displayName || category)).toBeInTheDocument();
});
const topicGroups = screen.queryAllByTestId('topic-group');
// For the new provider there should be a section for archived topics
expect(topicGroups).toHaveLength(
provider === DiscussionProvider.LEGACY
? categories.length
: categories.length + 1,
);
});
if (provider === DiscussionProvider.OPEN_EDX) {
it('displays archived topics', async () => {
await setupMockResponse();
renderComponent();
const archivedTopicGroup = screen.queryAllByTestId('topic-group').pop();
expect(archivedTopicGroup).toHaveTextContent(/archived/i);
const archivedTopicLinks = within(archivedTopicGroup).queryAllByRole('link');
expect(archivedTopicLinks).toHaveLength(2);
});
}
it('displays courseware topics', async () => {
await setupMockResponse();
renderComponent();
inContextTopics.forEach(topic => {
expect(screen.queryByText(topic.name)).toBeInTheDocument();
});
});
it('clicking on courseware topic (category) takes to category page', async () => {
await setupMockResponse();
renderComponent();
const categoryName = categories[0].displayName || categories[0];
const categoryPath = provider === 'legacy' ? categoryName : categories[0].id;
const topic = await screen.findByText(categoryName);
fireEvent.click(topic);
expect(lastLocation.pathname.endsWith(`/category/${categoryPath}`)).toBeTruthy();
});
});
});
Example #21
Source File: withHandlerGenerator.test.jsx From covid with GNU General Public License v3.0 | 4 votes |
test("withHandlerGenerator correctly generates a HOC to create a Wrapped component with data as a prop", async () => {
// Generate a testing HOC
const withIndex = (WrappedComponent, name = "index") =>
withHandlerGenerator(
withBackendHandlerHOC,
({ testParam }) => ({ testParam }),
({ testParam }, Handler, setIndex) => {
const handler = new Handler(testParam);
return handler.index(setIndex);
},
name,
WrappedComponent
);
// Use the generated testing HOC to pass `index` as a prop
const TestComponent = withIndex(
({ index: index_, role, testParam, ...props }) => {
const { testParam: testParamIndex, ...index } = index_;
return (
<span role={role}>
<span data-testid="index" {...index} />
<span data-testid="testParam" value={testParam} />
<span data-testid="testParamIndex" value={testParamIndex} />
</span>
);
}
);
let rendered;
await act(async () => {
const paramValue = "test-value";
rendered = render(
<TestComponent role="test-component" testParam={paramValue} />
);
// Initial <Loading/> state
const wrapperInitial = rendered.getByRole("wrapper");
expect(wrapperInitial).toBeInTheDocument();
const loading = within(wrapperInitial).getByRole("loading");
expect(loading).toBeInTheDocument();
await delay(20);
// Final state, with `index` as a prop in the wrapped component
const wrapper = rendered.getByRole("wrapper");
expect(wrapper).toBeInTheDocument();
const wrapped = within(wrapper).getByRole("test-component");
expect(wrapped).toBeInTheDocument();
const index = within(wrapped).getByRole("tested");
expect(index).toBeInTheDocument();
expect(index.getAttribute("data-testid")).toBe("index");
const testParam = within(wrapped).getByTestId("testParam");
expect(testParam).toBeInTheDocument();
const testParamIndex = within(wrapped).getByTestId("testParamIndex");
expect(testParamIndex).toBeInTheDocument();
expect(testParamIndex.getAttribute("value")).toBe(paramValue);
expect(testParamIndex.getAttribute("value")).toBe(
testParam.getAttribute("value")
);
});
await act(async () => {
const paramValue = "test-value-2";
rendered.rerender(
<TestComponent role="test-component" testParam={paramValue} />
);
await delay(0);
const testParam = rendered.getByTestId("testParam");
expect(testParam).toBeInTheDocument();
const testParamIndex = rendered.getByTestId("testParamIndex");
expect(testParamIndex).toBeInTheDocument();
expect(testParamIndex.getAttribute("value")).toBe(paramValue);
expect(testParamIndex.getAttribute("value")).toBe(
testParam.getAttribute("value")
);
});
});
Example #22
Source File: swap_card.spec.js From astroport-lbp-frontend with MIT License | 4 votes |
describe('SwapCard', () => {
const pair = buildPair({
contractAddr: 'terra1',
tokenContractAddr: 'terra2'
});
const saleTokenInfo = {
symbol: 'FOO',
decimals: 5
};
const ustExchangeRate = 0.99;
let onSwapTxMined;
beforeEach(() => {
onSwapTxMined = jest.fn();
});
function renderCard({ ustPrice } = {}) {
render(
<SwapCard
pair={pair}
saleTokenInfo={saleTokenInfo}
ustExchangeRate={ustExchangeRate}
ustPrice={ustPrice || new Dec(1)}
onSwapTxMined={onSwapTxMined}
/>
);
}
it('runs simulation, populates "to" field with simulated amount received, and calculates price impact', async () => {
getSimulation.mockResolvedValue({
return_amount: '200000000' // 0.50 UST valuation
});
getBalance.mockResolvedValue(2000 * 1e6); // Simulation does a basic balance check
renderCard({ ustPrice: new Dec(0.49) });
// Wait for balance
await waitForBalances({ fromBalance: '2,000' });
const fromInput = screen.getByLabelText('From');
const toInput = screen.getByLabelText('To (estimated)');
await act(async () => {
// We need to delay between inputs otherwise we end up with a field value of "1"
await userEvent.type(fromInput, '1000', { delay: 1 });
});
// "From" value is correctly converted to USD
const fromField = fromInput.closest('.border');
expect(within(fromField).getByText('($990.00)')).toBeInTheDocument(); // 1000 * 0.99
// "To" value is properly set to value returned by simulation
expect(toInput).toHaveDisplayValue('2000');
// "To" value is correctly converted to USD
const toField = toInput.closest('.border');
expect(within(toField).getByText('($970.20)')).toBeInTheDocument(); // 2000 * 0.49 * .99
// Simulated price is $0.01 higher than the spot price ($0.49),
// so the price impact is $0.01/$0.49 = 0.0204
expect(getDescriptionByTermEl(screen.getByText('Price Impact'))).toHaveTextContent('2.04%');
expect(getSimulation).toHaveBeenCalledWith(
mockTerraClient,
'terra1',
new Int(1000000000),
{
native_token: {
denom: 'uusd'
}
}
);
});
it('runs reverse simulation and populates "from" field with simulated amount required', async () => {
getReverseSimulation.mockResolvedValue({
offer_amount: '42000000'
});
// Simulation does a basic balance check
getBalance.mockResolvedValue(2000 * 1e6);
getTokenBalance.mockResolvedValue(2000 * 1e6);
renderCard({ ustPrice: new Dec(5.95) });
const toInput = screen.getByLabelText('To (estimated)');
// Wait for balances
await waitForBalances({ fromBalance: '2,000' });
await act(async () => {
await userEvent.type(toInput, '7');
});
// "From" value is properly set to value returned by reverse simulation
expect(screen.getByLabelText('From')).toHaveDisplayValue('42');
// Simulated price is $0.05 higher than the spot price ($5.95),
// so the price impact is $0.05/$5.95 = 0.0084
expect(getDescriptionByTermEl(screen.getByText('Price Impact'))).toHaveTextContent('0.84%');
expect(getReverseSimulation).toHaveBeenCalledWith(
mockTerraClient,
'terra1',
new Int(700000),
{
token: {
contract_addr: 'terra2'
}
}
);
});
it('runs new simulation when assets are reversed', async () => {
getSimulation.mockImplementation((_, pairAddress, amount, offerAssetInfo) => {
if(offerAssetInfo.native_token) {
// Mocked response when offer asset is the native token
return {
return_amount: '210000000' // 5 decimals
};
} else {
// Mocked response when offer asset is the sale token
return {
return_amount: '2000000' // 6 decimals
}
}
});
getBalance.mockResolvedValue(2000 * 1e6);
getTokenBalance.mockResolvedValue(2000 * 1e6);
renderCard({ ustPrice: new Dec(.48) });
// Wait for balances
await waitForBalances({ fromBalance: '2,000' });
// First enter a from value (UST -> FOO)
const fromInput = screen.getByLabelText('From');
await act(async () => {
await userEvent.type(fromInput, '4');
});
// Assert simulated value set
expect(screen.getByLabelText('To (estimated)')).toHaveDisplayValue('2100');
// Reverse the assets (FOO -> UST)
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Reverse assets' }));
});
// "To" value is properly set to value returned by simulation
expect(screen.getByLabelText('To (estimated)')).toHaveDisplayValue('2');
// Simulated price is $0.02 higher than the spot price ($0.48),
// so the price impact is $0.02/$0.48 = 0.0417
expect(getDescriptionByTermEl(screen.getByText('Price Impact'))).toHaveTextContent('4.17%');
// First simulation when initial "from" amount was entered
expect(getSimulation).toHaveBeenCalledWith(
mockTerraClient,
'terra1',
new Int(4 * 1e6), // 6 decimals
{
native_token: {
denom: 'uusd'
}
}
);
// Second simulation when "from" asset was changed
expect(getSimulation).toHaveBeenCalledWith(
mockTerraClient,
'terra1',
new Int(4 * 1e5), // 5 decimals
{
token: {
contract_addr: 'terra2'
}
}
);
});
it('performs native -> token swap, displays success message, and updates balances', async () => {
// Simulation is performed on input change
getSimulation.mockResolvedValue({
return_amount: String(5 * 1e5)
});
// Before balances
getBalance.mockResolvedValueOnce(2000000);
getTokenBalance.mockResolvedValueOnce(0);
// After balances
getBalance.mockResolvedValueOnce(1000000);
getTokenBalance.mockResolvedValueOnce(5 * 1e5);
// Mock fee fetching
const fee = jest.fn();
estimateFee.mockResolvedValue(fee);
// Successful post
postMsg.mockResolvedValue({ txhash: '123ABC' });
// Stub out balance check
sufficientBalance.mockResolvedValue(true);
renderCard();
// Initial balances
await waitForBalances({ fromBalance: '2', toBalance: '0' });
const fromInput = screen.getByLabelText('From');
await act(async () => {
await userEvent.type(fromInput, '1');
});
// Mock mined tx to trigger balance update
mockTerraClient.tx.txInfo.mockResolvedValue({});
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Swap' }));
});
expect(screen.getByText('Transaction Complete')).toBeInTheDocument();
const txLink = screen.getByRole('link', { name: '123ABC' });
expect(txLink).toBeInTheDocument();
expect(txLink.getAttribute('href')).toEqual('https://finder.terra.money/testnet/tx/123ABC');
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Continue' }));
});
// New balances
await waitForBalances({ fromBalance: '1', toBalance: '5' });
// Estimates fee and posts message with estimated fee
const msg = buildSwapFromNativeTokenMsg({
walletAddress: 'terra42',
pair,
intAmount: new Int(1e6)
});
expect(estimateFee).toHaveBeenCalledTimes(1);
expect(estimateFee).toHaveBeenCalledWith(mockTerraClient, msg);
expect(postMsg).toHaveBeenCalledTimes(1);
expect(postMsg).toHaveBeenCalledWith(mockTerraClient, { msg, fee });
// Fetches tx info
expect(mockTerraClient.tx.txInfo).toHaveBeenCalledWith('123ABC');
// Invokes callback
expect(onSwapTxMined).toHaveBeenCalledTimes(1);
});
it('performs token -> native token swap, displays success message, and updates balances', async () => {
// Simulation is performed on input change
getSimulation.mockResolvedValue({
return_amount: String(1e6)
});
// Before balances
getTokenBalance.mockResolvedValueOnce(10 * 1e5);
getBalance.mockResolvedValueOnce(0);
// After balances
getTokenBalance.mockResolvedValueOnce(5 * 1e5);
getBalance.mockResolvedValueOnce(1e6);
// Mock fee fetching
const fee = jest.fn();
estimateFee.mockResolvedValue(fee);
// Successful post
postMsg.mockResolvedValue({ txhash: 'ABC123' });
// Stub out balance check
sufficientBalance.mockResolvedValue(true);
renderCard();
// Initial balances
await waitForBalances({ fromBalance: '0', toBalance: '10' });
// Reverse the assets (FOO -> UST)
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Reverse assets' }));
});
const fromInput = screen.getByLabelText('From');
await act(async () => {
await userEvent.type(fromInput, '5');
});
// Mock mined tx to trigger balance update
mockTerraClient.tx.txInfo.mockResolvedValue({});
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Swap' }));
});
expect(screen.getByText('Transaction Complete')).toBeInTheDocument();
const txLink = screen.getByRole('link', { name: 'ABC123' });
expect(txLink).toBeInTheDocument();
expect(txLink.getAttribute('href')).toEqual('https://finder.terra.money/testnet/tx/ABC123');
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Continue' }));
});
// New balances
await waitForBalances({ fromBalance: '5', toBalance: '1' });
// Estimates fee and posts message with estimated fee
const msg = buildSwapFromContractTokenMsg({
walletAddress: 'terra42',
pair,
intAmount: new Int(5e5)
});
expect(estimateFee).toHaveBeenCalledTimes(1);
expect(estimateFee).toHaveBeenCalledWith(mockTerraClient, msg);
expect(postMsg).toHaveBeenCalledTimes(1);
expect(postMsg).toHaveBeenCalledWith(mockTerraClient, { msg, fee });
// Fetches tx info
expect(mockTerraClient.tx.txInfo).toHaveBeenCalledWith('ABC123');
});
it('performs swap after setting from amount to balance less fees when swapping from native token', async () => {
getBalance.mockResolvedValue(new Int(1000 * 1e6));
getTokenBalance.mockResolvedValue(new Int(0));
const fee = new StdFee(200000, new Coins(
[new Coin('uusd', 999999)]
));
feeForMaxNativeToken.mockResolvedValue(fee);
// Setting max from asset triggers a forward simulation
getSimulation.mockResolvedValue({ return_amount: '500000000' });
// Successful post
postMsg.mockResolvedValue({ txhash: '123ABC' });
// Stub out balance check
sufficientBalance.mockResolvedValue(true);
renderCard();
// Wait for balances to load
await waitForBalances({ fromBalance: '1,000' });
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Max' }));
});
// "From" value is properly set to value balance less fees
expect(screen.getByLabelText('From')).toHaveDisplayValue('999.000001');
// Perform swap
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Swap' }));
});
expect(screen.getByText('Transaction Complete')).toBeInTheDocument();
// Posts message with max fee
const msg = buildSwapFromNativeTokenMsg({
walletAddress: 'terra42',
pair,
intAmount: new Int(999000001)
});
expect(postMsg).toHaveBeenCalledTimes(1);
expect(postMsg).toHaveBeenCalledWith(mockTerraClient, { msg, fee });
// Does not estimate fee for from amount
// (this is calculated differently for "max" amount)
expect(estimateFee).not.toHaveBeenCalled();
});
it('performs swap after setting from amount to balance of contract token', async () => {
getBalance.mockResolvedValue(new Int(1000 * 1e6));
getTokenBalance.mockResolvedValue(new Int(5000 * 1e5));
const fee = new StdFee(200000, new Coins(
[new Coin('uusd', 30000)]
));
estimateFee.mockResolvedValue(fee);
// Setting max from asset triggers a forward simulation
getSimulation.mockResolvedValue({ return_amount: '1000000000' });
// Successful post
postMsg.mockResolvedValue({ txhash: '123ABC' });
// Stub out balance check
sufficientBalance.mockResolvedValue(true);
renderCard();
// Wait for balances to load
await waitForBalances({ fromBalance: '1,000', toBalance: '5,000' });
// Reverse the assets (FOO -> UST)
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Reverse assets' }));
});
// Use max FOO tokens
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Max' }));
});
// "From" value is properly set to entire token balance
expect(screen.getByLabelText('From')).toHaveDisplayValue('5000');
// Perform swap
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Swap' }));
});
expect(screen.getByText('Transaction Complete')).toBeInTheDocument();
// Posts message with max contract tokens
// and still estimates fee (uusd)
const msg = buildSwapFromContractTokenMsg({
walletAddress: 'terra42',
pair,
intAmount: new Int(5000 * 1e5)
});
expect(estimateFee).toHaveBeenCalledTimes(1);
expect(estimateFee).toHaveBeenCalledWith(mockTerraClient, msg);
expect(postMsg).toHaveBeenCalledTimes(1);
expect(postMsg).toHaveBeenCalledWith(mockTerraClient, { msg, fee });
});
it('conveys error state to user and does not invoke onSwapTxMined callback if extension responds with error when sending message', async() => {
// Simulation is performed on input change
getSimulation.mockResolvedValue({
return_amount: String(1e6)
});
getBalance.mockResolvedValue(10 * 1e6);
getTokenBalance.mockResolvedValue(0);
sufficientBalance.mockResolvedValue(true);
// Failed post
postMsg.mockRejectedValue({ code: 1 });
renderCard();
// Wait for balances
await waitForBalances({ fromBalance: '10' });
await act(async () => {
await userEvent.type(screen.getByLabelText('From'), '5');
});
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Swap' }));
});
expect(screen.queryByText('Error submitting transaction')).toBeInTheDocument();
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Continue' }));
});
expect(screen.queryByText('Error submitting transaction')).not.toBeInTheDocument();
// Does not invoke callback
expect(onSwapTxMined).not.toHaveBeenCalled();
});
it('displays and reports error when an error is thrown while selecting max balance', async() => {
getBalance.mockResolvedValue(new Int(1000 * 1e6));
getTokenBalance.mockResolvedValue(new Int(0));
const mockError = jest.fn();
feeForMaxNativeToken.mockRejectedValue(mockError);
renderCard();
// Wait for balances to load
await waitForBalances({ fromBalance: '1,000' });
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Max' }));
});
expect(screen.queryByText('Unable to swap max balance')).toBeInTheDocument();
expect(reportException).toHaveBeenCalledTimes(1);
expect(reportException).toHaveBeenCalledWith(mockError);
});
it('displays and reports error when simulation fails', async () => {
const mockError = jest.fn();
getSimulation.mockRejectedValue(mockError);
getBalance.mockResolvedValue(2000 * 1e6); // Simulation does a basic balance check
renderCard({ ustPrice: new Dec(0.49) });
// Wait for balance
await waitForBalances({ fromBalance: '2,000' });
const fromInput = screen.getByLabelText('From');
const toInput = screen.getByLabelText('To (estimated)');
await act(async () => {
await userEvent.type(fromInput, '1');
});
expect(screen.queryByText('Simulation failed')).toBeInTheDocument();
// "To" value is not set
expect(toInput).toHaveDisplayValue('');
// "To" value is still $0
const toField = toInput.closest('.border');
expect(within(toField).getByText('($0.00)')).toBeInTheDocument();
// Price impact is not calculated or displayed
expect(screen.queryByText('Price Impact')).not.toBeInTheDocument();
// Error is reported
expect(reportException).toHaveBeenCalledTimes(1);
expect(reportException).toHaveBeenCalledWith(mockError);
});
it('runs simulation and calculates price impact when from balance is insufficient, but displays error and does not calculate fees', async () => {
getSimulation.mockResolvedValue({
return_amount: '200000000' // 0.50 UST valuation
});
getBalance.mockResolvedValue(50 * 1e6);
renderCard({ ustPrice: new Dec(0.49) });
// Wait for balance
await waitForBalances({ fromBalance: '50' });
const fromInput = screen.getByLabelText('From');
const toInput = screen.getByLabelText('To (estimated)');
await act(async () => {
// We need to delay between inputs otherwise we end up with a field value of "1"
await userEvent.type(fromInput, '1000', { delay: 1 });
});
// "From" value is correctly converted to USD
const fromField = fromInput.closest('.border');
expect(within(fromField).getByText('($990.00)')).toBeInTheDocument(); // 1000 * 0.99
// "To" value is properly set to value returned by simulation
expect(toInput).toHaveDisplayValue('2000');
// "To" value is correctly converted to USD
const toField = toInput.closest('.border');
expect(within(toField).getByText('($970.20)')).toBeInTheDocument(); // 2000 * 0.49 * .99
// Simulated price is $0.01 higher than the spot price ($0.49),
// so the price impact is $0.01/$0.49 = 0.0204
expect(getDescriptionByTermEl(screen.getByText('Price Impact'))).toHaveTextContent('2.04%');
expect(screen.queryByText('Not enough UST')).toBeInTheDocument();
expect(getSimulation).toHaveBeenCalledWith(
mockTerraClient,
'terra1',
new Int(1000000000),
{
native_token: {
denom: 'uusd'
}
}
);
// Fees should have been estimated for each key stroke up until "10",
// then "100" and "1000" exceeded the balance of 50
expect(estimateFee).toHaveBeenCalledTimes(2);
});
it('displays pending state while waiting for tx to be mined', async () => {
jest.useFakeTimers();
// Simulation is performed on input change
getSimulation.mockResolvedValue({
return_amount: String(5 * 1e5)
});
// Before balances
getBalance.mockResolvedValueOnce(2000000);
getTokenBalance.mockResolvedValueOnce(0);
// After balances
getBalance.mockResolvedValueOnce(1000000);
getTokenBalance.mockResolvedValueOnce(5 * 1e5);
// Successful post
postMsg.mockResolvedValue({ txhash: '123ABC' });
// Stub out balance check
sufficientBalance.mockResolvedValue(true);
renderCard();
// Initial balances
await waitForBalances({ fromBalance: '2', toBalance: '0' });
const fromInput = screen.getByLabelText('From');
await act(async () => {
await userEvent.type(fromInput, '1');
});
// Mock pending tx (404)
mockTerraClient.tx.txInfo.mockRejectedValue();
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Swap' }));
});
expect(screen.getByText('Please Wait')).toBeInTheDocument();
let txLink = screen.getByRole('link', { name: '123ABC' });
expect(txLink).toBeInTheDocument();
expect(txLink.getAttribute('href')).toEqual('https://finder.terra.money/testnet/tx/123ABC');
// Mock mined tx
mockTerraClient.tx.txInfo.mockResolvedValue({});
// Blockchain is polled every 5s until tx is mined
act(() => {
jest.advanceTimersByTime(5000);
});
expect(await screen.findByText('Transaction Complete')).toBeInTheDocument();
txLink = screen.getByRole('link', { name: '123ABC' });
expect(txLink).toBeInTheDocument();
expect(txLink.getAttribute('href')).toEqual('https://finder.terra.money/testnet/tx/123ABC');
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Continue' }));
});
// New balances
await waitForBalances({ fromBalance: '1', toBalance: '5' });
// Invokes callback
expect(onSwapTxMined).toHaveBeenCalledTimes(1);
});
});
Example #23
Source File: app.spec.js From astroport-lbp-frontend with MIT License | 4 votes |
describe('App', () => {
it('renders Scheduled and Previous Token Sales cards', async () => {
const dateNowSpy = jest
.spyOn(Date, 'now')
.mockImplementation(() => new Date(2021, 5, 9).getTime());
// Mock toLocaleString to always use en-US locale in EDT timezone
const toLocaleStringSpy = jest.spyOn(Date.prototype, 'toLocaleString');
toLocaleStringSpy.mockImplementation(
function (locale, options) {
return new Intl.DateTimeFormat('en-US', { ...options, timeZone: 'America/New_York' }).format(this);
}
)
const currentPair = buildPair({
startTime: Math.floor(Date.UTC(2021, 5, 8, 12)/1000),
endTime: Math.floor(Date.UTC(2021, 5, 10, 12)/1000),
tokenContractAddr: 'terra3',
contractAddr: 'terra3-pair-addr'
});
// This pair would be displayed as scheduled if permitted
const unpermittedPair = buildPair({
startTime: Math.floor(Date.UTC(2021, 5, 10, 12)/1000),
endTime: Math.floor(Date.UTC(2021, 5, 14, 12)/1000),
tokenContractAddr: 'terra4',
contractAddr: 'terra4-pair-addr'
});
getLBPs.mockResolvedValue([
buildPair({
startTime: Math.floor(Date.UTC(2021, 0, 1, 12)/1000),
endTime: Math.floor(Date.UTC(2021, 0, 4, 12)/1000),
tokenContractAddr: 'terra1',
contractAddr: 'terra1-pair-addr'
}),
buildPair({
startTime: Math.floor(Date.UTC(2021, 5, 10, 12)/1000),
endTime: Math.floor(Date.UTC(2021, 5, 14, 12)/1000),
tokenContractAddr: 'terra2',
contractAddr: 'terra2-pair-addr'
}),
unpermittedPair,
currentPair
]);
getPairInfo.mockResolvedValue(currentPair);
getTokenInfo.mockImplementation((_, address) => (
{
terra1: {
name: 'Foo'
},
terra2: {
name: 'Bar'
},
terra3: {
name: 'Baz'
},
terra4: {
name: 'Bad'
}
}[address]
));
render(<App />);
// Heading with sale token name
expect(await screen.findByText('Baz Token Sale')).toBeInTheDocument();
// Current token info component
expect(await screen.findByText('Current Token Info')).toBeInTheDocument();
// Tokens are in the correct cards with the correct time/dates
const scheduledCard = (await screen.findByText('Scheduled Token Sales')).closest('div')
const previousCard = (await screen.findByText('Previous Token Sales')).closest('div')
const barCell = await within(scheduledCard).findByText('Bar');
expect(barCell).toBeInTheDocument();
expect(within(barCell.closest('tr')).queryByText('06/10/2021, 08:00 AM EDT')).toBeInTheDocument();
const fooCell = await within(previousCard).findByText('Foo')
expect(fooCell).toBeInTheDocument();
expect(within(fooCell.closest('tr')).queryByText('01/01/2021 - 01/04/2021')).toBeInTheDocument();
// Tokens are not present in the wrong cards
expect(within(scheduledCard).queryByText('Foo')).toBeNull();
expect(within(scheduledCard).queryByText('Baz')).toBeNull();
expect(within(previousCard).queryByText('Bar')).toBeNull();
expect(within(previousCard).queryByText('Baz')).toBeNull();
// It should have fetched info for the current sale
expect(getPairInfo).toHaveBeenCalledTimes(1);
expect(getPairInfo).toHaveBeenCalledWith(expect.anything(), 'terra3-pair-addr');
// Unpermitted pair should never be displayed
expect(screen.queryByText('Bad')).not.toBeInTheDocument();
dateNowSpy.mockRestore();
});
it('displays partial wallet address after successful browser extension connection', async () => {
connectExtension.mockResolvedValue({ address: 'terra1234567890' });
const currentPair = buildPair({ contractAddr: 'terra1-pair-addr' });
getLBPs.mockResolvedValue([
currentPair
]);
getPairInfo.mockResolvedValue(currentPair);
getTokenInfo.mockResolvedValue({
name: 'Foo'
});
render(<App />);
// Wait for data to load and Connect Wallet button to become visible
await screen.findByText('Connect Wallet');
// Wallet address should not yet be displayed
expect(screen.queryByText('567890')).toBeNull();
await act(async () => {
await userEvent.click(screen.getByText('Connect Wallet'));
})
expect(screen.getByText('terra1...567890')).toBeInTheDocument();
});
it('automatically reconnects extension if it was connected previously', async () => {
const getItemSpy = jest.spyOn(window.localStorage.__proto__, 'getItem');
getItemSpy.mockImplementation((key) => {
return {
terraStationExtensionPreviouslyConnected: true
}[key]
});
connectExtension.mockResolvedValue({ address: 'terra1234567890' });
const currentPair = buildPair({ contractAddr: 'terra1-pair-addr' });
getLBPs.mockResolvedValue([
currentPair
]);
getPairInfo.mockResolvedValue(currentPair);
getTokenInfo.mockResolvedValue({
name: 'Foo'
});
render(<App />);
expect(await screen.findByText('terra1...567890')).toBeInTheDocument();
expect(getItemSpy).toHaveBeenCalledTimes(1);
expect(getItemSpy).toHaveBeenCalledWith('terraStationExtensionPreviouslyConnected');
});
it('disconnects wallet', async () => {
const getItemSpy = jest.spyOn(window.localStorage.__proto__, 'getItem');
const removeItemSpy = jest.spyOn(window.localStorage.__proto__, 'removeItem');
getItemSpy.mockImplementation((key) => {
return {
terraStationExtensionPreviouslyConnected: true
}[key]
});
connectExtension.mockResolvedValue({ address: 'terra1234567890' });
const currentPair = buildPair({ contractAddr: 'terra1-pair-addr'} );
getLBPs.mockResolvedValue([
currentPair
]);
getPairInfo.mockResolvedValue(currentPair);
getTokenInfo.mockResolvedValue({
name: 'Foo'
});
render(<App />);
expect(await screen.findByText('terra1...567890')).toBeInTheDocument();
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: 'Disconnect wallet' }));
})
expect(screen.queryByText('terra1...567890')).not.toBeInTheDocument();
expect(getItemSpy).toHaveBeenCalledTimes(1);
expect(getItemSpy).toHaveBeenCalledWith('terraStationExtensionPreviouslyConnected');
expect(removeItemSpy).toHaveBeenCalledTimes(1);
expect(removeItemSpy).toHaveBeenCalledWith('terraStationExtensionPreviouslyConnected');
});
});
Example #24
Source File: PeoplePage.test.jsx From sgmr-service with MIT License | 4 votes |
describe('People page', () => {
const mockAxios = new MockAdapter(axios);
beforeEach(() => {
mockAxios.reset();
NotificationBanner.mockReturnValue(null);
});
it('should show the list of people saved on your account', async () => {
mockAxios
.onGet(`${PEOPLE_URL}`, 'people')
.reply(200, MockedAccountPeopleList);
renderPage();
await waitFor(() => {
expect(screen.getByText('Saved people')).toBeInTheDocument();
expect(screen.getByText('Fred Flintstone')).toBeInTheDocument();
expect(screen.getByText('1Fred')).toBeInTheDocument();
expect(screen.getByText('01/01/2025')).toBeInTheDocument();
expect(screen.getByText('Barney Rubble')).toBeInTheDocument();
expect(screen.getByText('2Barney')).toBeInTheDocument();
expect(screen.getByText('01/12/2030')).toBeInTheDocument();
expect(screen.getByText('Add new person')).toHaveClass('govuk-button govuk-button--secondary');
});
});
it('should show the the h1 & add person button if there are 0 people saved on your account', async () => {
mockAxios
.onGet(`${PEOPLE_URL}`, 'people')
.reply(204);
renderPage();
await waitFor(() => {
expect(screen.getByText('Saved people')).toBeInTheDocument();
expect(screen.getByText('Add new person')).toHaveClass('govuk-button govuk-button--secondary');
});
});
it('should sort the list of people alphabetically by last name, firstname', async () => {
mockAxios
.onGet(`${PEOPLE_URL}`, 'people')
.reply(200, MockedAccountPeopleUnsortedList);
renderPage();
await waitFor(() => {
const rows = screen.getAllByTestId('row');
// test first names appear in correct order based on sort by lastName>firstName
expect(within(rows[0]).queryByText('Fred Flintstone')).toBeInTheDocument();
expect(within(rows[1]).queryByText('Pebbles Flintstone')).toBeInTheDocument();
expect(within(rows[2]).queryByText('Wilma Flintstone')).toBeInTheDocument();
expect(within(rows[3]).queryByText('BamBam Rubble')).toBeInTheDocument();
expect(within(rows[4]).queryByText('Barney Rubble')).toBeInTheDocument();
expect(within(rows[5]).queryByText('Betty Rubble')).toBeInTheDocument();
});
});
it('should take you to the add person form when you click add new person', async () => {
mockAxios
.onGet(`${PEOPLE_URL}`, 'people')
.reply(200, MockedAccountPeopleList);
renderPage();
await waitFor(() => {
expect(screen.getByText('Add new person')).toHaveClass('govuk-button govuk-button--secondary');
fireEvent.click(screen.getByText('Add new person'));
});
await waitFor(() => {
expect(mockHistoryPush).toHaveBeenCalledWith('/people/save-person/page-1');
});
});
it('should take you to the DELETE person page when you click remove', async () => {
mockAxios
.onGet(`${PEOPLE_URL}`, 'people')
.reply(200, MockedAccountPeopleList);
renderPage();
await waitFor(() => {
expect(screen.getByText('Fred Flintstone')).toBeInTheDocument();
expect(screen.getAllByText('Remove')).toHaveLength(3);
fireEvent.click(screen.getAllByText('Remove')[0]);
});
await waitFor(() => {
expect(mockHistoryPush).toHaveBeenCalledWith('/people/1/delete');
});
});
it('should take you to the edit person form when you click update', async () => {
mockAxios
.onGet(`${PEOPLE_URL}`, 'people')
.reply(200, MockedAccountPeopleList);
renderPage();
await waitFor(() => {
expect(screen.getByText('Fred Flintstone')).toBeInTheDocument();
expect(screen.getAllByText('Update')).toHaveLength(3);
fireEvent.click(screen.getAllByText('Update')[2]);
});
await waitFor(() => {
expect(mockHistoryPush).toHaveBeenCalledWith('/people/edit-person/page-1', { peopleId: '3', source: 'edit' });
});
});
});
Example #25
Source File: App.test.js From Simplify-Testing-with-React-Testing-Library with MIT License | 4 votes |
describe('Integration: Budget App', () => {
function setOneDollarIncome() {
user.click(screen.getByText(/set income/i));
user.type(screen.getByRole('spinbutton'), '1');
user.click(screen.getByText(/submit/i));
}
function createCarBudget(amount = '5') {
user.click(screen.getByText(/create new budget/i));
user.selectOptions(screen.getByRole('combobox', { name: /category/i }), [
screen.getByText('Auto'),
]);
user.type(screen.getByRole('spinbutton'), amount);
user.click(screen.getByText(/add budget/i));
}
test('SetIncome, given income amount, sets income', () => {
render(<App />);
setOneDollarIncome();
const leftOverBudget = screen.getByText(/left over:/i);
const leftOverBudgetAmount = within(leftOverBudget).getByText(/\$1/i);
expect(leftOverBudgetAmount).toBeInTheDocument();
expect(
screen.getByRole('heading', { name: /income: \$1/i })
).toBeInTheDocument();
});
describe('CreateNewBudget', () => {
test.each`
budgetAmount | spending | leftOver
${'4'} | ${'Spending: $5'} | ${'$-4'}
${'5'} | ${'Spending: $5'} | ${'$-4'}
${'6'} | ${'Spending: $10'} | ${'$-9'}
`(
'given budget, updates budget summary',
({ budgetAmount, spending, leftOver }) => {
render(<App />);
setOneDollarIncome();
createCarBudget(budgetAmount);
const leftOverBudget = screen.getByText(/left over:/i);
const leftOverBudgetAmount = within(leftOverBudget).getByText(leftOver);
expect(leftOverBudgetAmount).toBeInTheDocument();
expect(
screen.getByRole('heading', { name: spending })
).toBeInTheDocument();
}
);
test('given budget, displays budget chart', () => {
render(<App />);
setOneDollarIncome();
createCarBudget();
expect(screen.getByTestId('chart')).toBeInTheDocument();
});
});
describe('Budget', () => {
test('given budget, displays details', () => {
render(<App />);
setOneDollarIncome();
createCarBudget();
const budgetList = screen.getByRole('listitem');
expect(within(budgetList).getByText(/auto/i)).toBeInTheDocument();
expect(
screen.getByRole('heading', { name: /\$0 of \$5/i })
).toBeInTheDocument();
});
test('given budget expense, updates budget progress', () => {
render(<App />);
setOneDollarIncome();
createCarBudget();
user.click(screen.getByRole('button', { name: /arrowright/i }));
expect(
screen.getByRole('heading', { name: /\$5 of \$5/i })
).toBeInTheDocument();
});
});
test('DeleteBudget, given deleted budget, budget removed from DOM', () => {
render(<App />);
setOneDollarIncome();
createCarBudget();
user.click(screen.getByLabelText(/trash can/i));
expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
});
});
Example #26
Source File: verify.test.js From treetracker-admin-client with GNU Affero General Public License v3.0 | 4 votes |
describe('Verify', () => {
let growerApi;
let captureApi;
//mock the growers api
growerApi = require('../../api/growers').default;
growerApi.getCount = () => {
log.debug('mock getCount:');
return Promise.resolve({ count: 2 });
};
growerApi.getGrower = () => {
log.debug('mock getGrower:');
return Promise.resolve(GROWER);
};
growerApi.getGrowerRegistrations = () => {
log.debug('mock getGrowerRegistrations:');
return Promise.resolve([]);
};
growerApi.getGrowerSelfies = (id) => {
log.debug('mock getGrowerSelfies:');
return Promise.resolve([{ planterPhotoUrl: '' }, { planterPhotoUrl: '' }]);
};
// mock the treeTrackerApi
captureApi = require('../../api/treeTrackerApi').default;
captureApi.getCaptureImages = () => {
log.debug('mock getCaptureImages:');
return Promise.resolve(CAPTURES);
};
captureApi.getCaptureCount = () => {
log.debug('mock getCaptureCount:');
return Promise.resolve({ count: 4 });
};
captureApi.getCaptureById = (_id) => {
log.debug('mock getCaptureById:');
return Promise.resolve(CAPTURE);
};
captureApi.getSpecies = () => {
log.debug('mock getSpecies:');
return Promise.resolve(SPECIES);
};
captureApi.getSpeciesById = (_id) => {
log.debug('mock getSpeciesById:');
return Promise.resolve(SPECIES[0]);
};
captureApi.getCaptureCountPerSpecies = () => {
log.debug('mock getCaptureCountPerSpecies:');
return Promise.resolve({ count: 7 });
};
captureApi.getTags = () => {
log.debug('mock getTags:');
return Promise.resolve(TAGS);
};
captureApi.getTagById = (_id) => {
log.debug('mock getTagById:');
return Promise.resolve(TAG);
};
captureApi.getOrganizations = () => {
log.debug('mock getOrganizations:');
return Promise.resolve(ORGS);
};
describe('with default values', () => {
beforeEach(async () => {
render(
<ThemeProvider theme={theme}>
<BrowserRouter>
<AppProvider value={{ orgList: ORGS }}>
<GrowerContext.Provider value={growerValues}>
<VerifyProvider value={verifyValues}>
<SpeciesProvider value={speciesValues}>
<TagsContext.Provider value={tagsValues}>
<Verify />
</TagsContext.Provider>
</SpeciesProvider>
</VerifyProvider>
</GrowerContext.Provider>
</AppProvider>
</BrowserRouter>
</ThemeProvider>
);
await act(() => captureApi.getCaptureImages());
await act(() => captureApi.getCaptureCount());
// await act(() => captureApi.getTags());
});
afterEach(cleanup);
it('renders filter top', () => {
const filter = screen.getByRole('button', { name: /filter/i });
userEvent.click(filter);
// screen.logTestingPlaygroundURL();
const verifyStatus = screen.getByLabelText(/awaiting verification/i);
expect(verifyStatus).toBeInTheDocument();
const tokenStatus = screen.getByLabelText(/token status/i);
expect(tokenStatus).toBeInTheDocument();
});
it('renders number of applied filters', async () => {
const filter = screen.getByRole('button', { name: /filter 1/i });
userEvent.click(filter);
expect(screen.getByText(/awaiting verification/i)).toBeInTheDocument();
//data won't actually be filtered but filters should be selected
//why was this set to expect 2 filters?
expect(verifyValues.filter.countAppliedFilters()).toBe(1);
let dropdown = screen.getByTestId('org-dropdown');
expect(dropdown).toBeInTheDocument();
let button = within(dropdown).getByRole('button', {
name: /all/i,
});
userEvent.click(button);
// the actual list of orgs is displayed in a popup that is not part of FilterTop
// this list is the default list
const orglist = screen.getByRole('listbox');
const orgSelected = screen.getByRole('option', { name: /not set/i });
userEvent.selectOptions(orglist, orgSelected);
userEvent.click(screen.getByText(/apply/i));
expect(screen.getByRole('button', { name: /filter 1/i })).toBeTruthy();
//this function is still returning 1
expect(verifyValues.filter.countAppliedFilters()).toBe(1);
});
// it('renders side panel', () => {
// // screen.logTestingPlaygroundURL();
// // expect(screen.getByText(/planters per page: 24/i));
// });
it('renders captures gallery', () => {
const pageSize = screen.getAllByText(/captures per page:/i);
expect(pageSize).toHaveLength(2);
expect(screen.getByText(/4 captures/i));
});
it('renders capture details', () => {
const captureDetails = screen.getAllByRole('button', {
name: /capture details/i,
});
expect(captureDetails).toHaveLength(4);
userEvent.click(captureDetails[0]);
expect(screen.getByText(/capture data/i)).toBeInTheDocument();
expect(screen.getByText(/grower identifier/i)).toBeInTheDocument();
expect(screen.getByText(/[email protected]/i)).toBeInTheDocument();
expect(screen.getByText(/device identifier/i)).toBeInTheDocument();
// expect(screen.getByText(/1 - abcdef123456/i)).toBeInTheDocument();
expect(screen.getByText(/verification status/i)).toBeInTheDocument();
expect(screen.getByText(/token status/i)).toBeInTheDocument();
});
it('renders grower details', () => {
const growerDetails = screen.getAllByRole('button', {
name: /grower details/i,
});
expect(growerDetails).toHaveLength(4);
userEvent.click(growerDetails[0]);
// screen.logTestingPlaygroundURL();
expect(screen.getByText(/country/i)).toBeInTheDocument();
expect(screen.getByText(/organization/i)).toBeInTheDocument();
expect(screen.getByText(/person ID/i)).toBeInTheDocument();
expect(screen.getByText(/ID:/i)).toBeInTheDocument();
expect(screen.getByText(/email address/i)).toBeInTheDocument();
expect(screen.getByText(/phone number/i)).toBeInTheDocument();
expect(screen.getByText(/registered/i)).toBeInTheDocument();
});
// it('renders edit planter', () => {
// const planterDetails = screen.getAllByRole('button', {
// name: /planter details/i,
// });
// userEvent.click(planterDetails[0]);
// screen.logTestingPlaygroundURL();
// //
// const editPlanter = screen.getByTestId(/edit-planter/i);
// expect(editPlanter).toBeInTheDocument();
// userEvent.click(editPlanter);
// });
});
});
Example #27
Source File: species.test.js From treetracker-admin-client with GNU Affero General Public License v3.0 | 4 votes |
describe('species management', () => {
let api;
let speciesValues;
// can be used to test routes and permissions
beforeEach(() => {
api = require('../../api/treeTrackerApi').default;
api.getSpecies = jest.fn(() => {
// log.debug('mock getSpecies:');
return Promise.resolve(SPECIES);
});
api.createSpecies = jest.fn(() => {
// log.debug('mock createSpecies');
return Promise.resolve({
id: 2,
name: 'water melon',
desc: 'fruit',
});
});
api.getCaptureCountPerSpecies = jest.fn(() => {
return Promise.resolve({ count: (Math.random() * 10) >> 0 });
});
speciesValues = {
speciesList: SPECIES,
speciesInput: '',
speciesDesc: '',
setSpeciesInput: () => {},
loadSpeciesList: () => {},
onChange: () => {},
isNewSpecies: () => {},
createSpecies: () => {},
getSpeciesId: () => {},
editSpecies: () => {},
deleteSpecies: () => {},
combineSpecies: () => {},
};
});
afterEach(cleanup);
describe('<SpeciesView /> renders page', () => {
beforeEach(async () => {
render(
<BrowserRouter>
<AppProvider>
<SpeciesProvider value={speciesValues}>
<SpeciesView />
</SpeciesProvider>
</AppProvider>
</BrowserRouter>,
);
await act(() => api.getSpecies());
});
afterEach(cleanup);
describe('it shows main page elements', () => {
it('then shows "Add New Species" button', () => {
expect(screen.getByText(/Add New Species/i)).toBeTruthy();
});
it('then shows "Combine Species" button', () => {
expect(screen.getByText(/Combine Species/i)).toBeTruthy();
});
it('species list should be 2', () => {
expect(speciesValues.speciesList).toHaveLength(3);
});
// shows a table with headers
// shows navigation menu
});
describe('when the "Add New Species" button is clicked', () => {
beforeEach(() => {
userEvent.click(screen.getByText(/Add New Species/i));
});
it('see popup with species detail form', () => {
expect(screen.getByText(/Species Detail/i)).toBeTruthy();
});
it('has inputs for name and description', () => {
const dialog = screen.getByRole(/dialog/i);
const item = within(dialog).getByLabelText(/name/i);
expect(item).toBeTruthy();
});
it('has buttons to save and cancel', () => {
const dialog = screen.getByRole(/dialog/i);
expect(within(dialog).getByText(/save/i)).toBeTruthy();
});
});
// [TODO]: MORE TESTS
// when the combine species button is clicked
//it fails if two species aren't selected
//opens if 2+ species are selected
//shows those species names in the popup
//has inputs for name and description
//has buttons to save and cancel
describe('when creating a new species', () => {
beforeEach(async () => {
// await api.createSpecies({ name: 'water melon' });
userEvent.click(screen.getByText(/Add New Species/i));
const dialog = screen.getByRole(/dialog/i);
const inputName = screen.getByLabelText(/name/i);
const inputDesc = screen.getByLabelText(/description/i);
const saveBtn = screen.getByText(/Save/i);
userEvent.type(inputName, 'water melon');
expect(inputName.value).toBe('water melon');
userEvent.type(inputDesc, 'test');
expect(inputDesc.value).toBe('test');
expect(screen.getByDisplayValue('water melon')).toBeTruthy();
expect(screen.getByDisplayValue('test')).toBeTruthy();
userEvent.click(saveBtn);
// mock-adding new species --- DOESN'T UPDATE state
// speciesValues.speciesList.push({
// id: 2,
// name: 'water melon',
// desc: 'fruit',
// });
// wait for it... to complete & dialog to close
waitForElementToBeRemoved(dialog);
//---- the last 2 tests work w/o this line but get act() errors in the console.
//---- the errors go away w/this line but then tests fail
});
afterEach(cleanup);
it('api.createSpecies should be called with "water melon"', () => {
expect(api.createSpecies.mock.calls[0][0].name).toBe('water melon');
});
// it('species list should be 3 (1 added)', () => {
// expect(speciesValues.speciesList).toHaveLength(3);
// });
// it('has 3 species', () => {
// const items = screen.getAllByTestId('species');
// // screen.logTestingPlaygroundURL();
// const speciesNames = items.map((el) => el.textContent);
// // console.log('speciesNames', speciesNames);
// expect(items).toHaveLength(3);
// });
// it('added species should show on the screen', () => {
// expect(screen.getByText('water melon')).toBeTruthy();
// });
});
});
});
Example #28
Source File: regions.test.js From treetracker-admin-client with GNU Affero General Public License v3.0 | 4 votes |
describe('region management', () => {
let treeTrackerApi;
let regionsApi;
let regionValues;
beforeEach(() => {
regionsApi = require('../../api/regions').default;
treeTrackerApi = require('../../api/treeTrackerApi').default;
regionsApi.getRegions = jest.fn(() => {
return Promise.resolve({
query: {
count: REGIONS.length,
},
regions: REGIONS,
});
});
regionsApi.getRegion = jest.fn((id) => {
const region = REGIONS.find((reg) => id === reg.id);
return Promise.resolve({ region });
});
regionsApi.getCollections = jest.fn(() => {
return Promise.resolve({
query: {
count: REGION_COLLECTIONS.length,
},
collections: REGION_COLLECTIONS,
});
});
regionsApi.getCollection = jest.fn((id) => {
const collection = REGION_COLLECTIONS.find((coll) => id === coll.id);
return Promise.resolve({ collection });
});
regionsApi.upload = jest.fn(() => {
return Promise.resolve(REGIONS[0]);
});
regionsApi.updateRegion = jest.fn(() => {
return Promise.resolve(REGIONS[0]);
});
regionsApi.updateCollection = jest.fn(() => {
return Promise.resolve(REGION_COLLECTIONS[0]);
});
treeTrackerApi.getOrganizations = jest.fn(() => {
return Promise.resolve(ORGS);
});
regionValues = {
regions: REGIONS,
collections: REGION_COLLECTIONS,
pageSize: 25,
regionCount: null,
collectionCount: null,
currentPage: 0,
filter: new FilterRegion(),
isLoading: false,
showCollections: false,
changePageSize: () => {},
changeCurrentPage: () => {},
changeSort: () => {},
setShowCollections: () => {},
loadRegions: () => {},
loadCollections: () => {},
getRegion: () => {},
upload: () => {},
updateRegion: () => {},
updateCollection: () => {},
updateFilter: () => {},
deleteRegion: () => {},
deleteCollection: () => {},
};
});
afterEach(cleanup);
describe('<Regions /> renders page', () => {
beforeEach(async () => {
render(
<BrowserRouter>
<AppProvider value={{ orgList: ORGS }}>
<RegionProvider value={regionValues}>
<RegionsView />
</RegionProvider>
</AppProvider>
</BrowserRouter>
);
await act(() => regionsApi.getRegions());
});
afterEach(cleanup);
describe('it shows main page elements', () => {
it('then shows "Upload" button', () => {
expect(screen.getByText(/Upload/i)).toBeTruthy();
});
it('species list should be 2', () => {
expect(regionValues.regions).toHaveLength(2);
});
// shows a table with headers
// shows navigation menu
});
describe('when the "Upload" button is clicked', () => {
beforeEach(() => {
userEvent.click(screen.getByText(/Upload/i));
});
it('see popup with upload region or collection form', () => {
expect(
screen.getByText(/Upload New Region or Collection/i)
).toBeTruthy();
});
it('has inputs for owner and region name property', () => {
const dialog = screen.getByRole(/dialog/i);
expect(within(dialog).getByLabelText(/owner/i)).toBeTruthy();
expect(
within(dialog).getByLabelText(/region name property/i)
).toBeTruthy();
});
it('has buttons to upload and cancel', () => {
const dialog = screen.getByRole(/dialog/i);
expect(
within(dialog).getByRole('button', { name: /upload/i })
).toBeTruthy();
});
});
describe('regions table', () => {
it('shows the table header', () => {
const table = screen.getByRole(/table/i);
expect(within(table).getByText(/name/i)).toBeTruthy();
expect(within(table).getByText(/owner/i)).toBeTruthy();
expect(within(table).getByText(/collection/i)).toBeTruthy();
expect(within(table).getByText(/properties/i)).toBeTruthy();
expect(within(table).getByText(/shown on org map/i)).toBeTruthy();
expect(within(table).getByText(/statistics calculated/i)).toBeTruthy();
});
it('shows a region record', () => {
const table = screen.getByRole(/table/i);
expect(within(table).getByText(REGIONS[0].name)).toBeTruthy();
expect(within(table).getAllByText(ORGS[0].name)).toBeTruthy();
expect(
within(table).getAllByText(REGION_COLLECTIONS[0].name)
).toBeTruthy();
expect(within(table).getByText(REGIONS[0].properties.Id)).toBeTruthy();
});
});
});
});
Example #29
Source File: organizations.test.js From treetracker-admin-client with GNU Affero General Public License v3.0 | 4 votes |
describe('CaptureFilter organizations', () => {
let api;
beforeEach(() => {
//mock the api
api = require('../../api/treeTrackerApi').default;
api.getOrganizations = () => {
// log.debug('mock getOrganizations:');
return Promise.resolve(ORGS);
};
});
describe('CaptureFilter', () => {
describe('w/o data in context', () => {
let component;
beforeEach(async () => {
component = (
<AppProvider>
<CaptureFilter />
</AppProvider>
);
});
afterEach(cleanup);
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(component, div);
ReactDOM.unmountComponentAtNode(div);
});
it('renders text "Verification Status" ', () => {
render(component);
expect(screen.getByText('Verification Status')).toBeInTheDocument();
});
it('renders "Start Date" input ', () => {
render(component);
let input = screen.getByRole('textbox', { name: 'Start Date' });
expect(input).toBeInTheDocument();
});
it('renders "End Date" input ', () => {
render(component);
let input = screen.getByRole('textbox', { name: 'End Date' });
expect(input).toBeInTheDocument();
});
it('renders Species dropdown ', () => {
render(component);
let dropdown = screen.getByTestId('species-dropdown');
expect(dropdown).toBeInTheDocument();
});
it('renders Tags dropdown ', () => {
render(component);
let dropdown = screen.getByTestId('tag-dropdown');
expect(dropdown).toBeInTheDocument();
});
it('renders Organization dropdown ', () => {
render(component);
let dropdown = screen.getByTestId('org-dropdown');
expect(dropdown).toBeInTheDocument();
});
it('renders default orgList when dropdown clicked ', () => {
render(component);
let dropdown = screen.getByTestId('org-dropdown');
expect(dropdown).toBeInTheDocument();
let button = within(dropdown).getByRole('button', {
name: /all/i,
});
userEvent.click(button);
// the actual list of orgs is displayed in a popup that is not part of CaptureFilter
// this list is the default list
const orglist = screen.getByRole('listbox');
const orgs = within(orglist).getAllByTestId('org-item');
const listItems = orgs.map((org) => org.textContent);
console.log('default orgList', listItems);
expect(orgs).toHaveLength(2);
});
});
describe('w/ data in context', () => {
let orgs;
let component;
beforeEach(async () => {
orgs = await api.getOrganizations();
component = (
<AppProvider value={{ orgList: orgs }}>
<CaptureFilter />
</AppProvider>
);
// render(component);
await act(() => api.getOrganizations());
});
afterEach(cleanup);
it('api loaded 2 organizations', () => {
expect(orgs).toHaveLength(2);
});
it('renders Organization dropdown ', () => {
render(component);
let dropdown = screen.getByTestId('org-dropdown');
expect(dropdown).toBeInTheDocument();
});
it('renders default orgList when dropdown clicked ', () => {
render(component);
let dropdown = screen.getByTestId('org-dropdown');
expect(dropdown).toBeInTheDocument();
let button = within(dropdown).getByRole('button', { name: /all/i });
userEvent.click(button);
// screen.logTestingPlaygroundURL();
// the actual list of orgs is displayed in a popup that is not part of CaptureFilter
const orglist = screen.getByRole('listbox');
const orgs = within(orglist).getAllByTestId('org-item');
const listItems = orgs.map((org) => org.textContent);
console.log('default orgList', listItems);
// two default options + two orgs
expect(orgs).toHaveLength(4);
});
});
// describe('context data renders in child', () => {
// let orgs;
// let component;
// beforeEach(async () => {
// component = (
// <AppProvider>
// <AppContext.Consumer>
// {(value) => <p>Received: {value.orgList}</p>}
// </AppContext.Consumer>
// </AppProvider>
// );
// render(component);
// await act(() => api.getOrganizations());
// });
// // just tests the mock api, not what's showing on the page
// it('api loaded 2 organizations', () => {
// expect(orgs).toHaveLength(2);
// });
// it('renders text "Dummy Org" ', () => {
// // screen.debug(); // shows structure in console
// screen.logTestingPlaygroundURL();
// // expect(screen.getByText(/^Received:/).textContent).toBe('Received: ');
// expect(screen.getByText('Dummy Org')).toBeInTheDocument();
// });
// });
});
});