@testing-library/react#getByRole JavaScript Examples

The following examples show how to use @testing-library/react#getByRole. 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: index.jsdom.test.jsx    From apps-todo-list with MIT License 6 votes vote down vote up
function getItems() {
    return screen.queryAllByRole('checkbox').map(checkbox => {
        const container = checkbox.parentNode;
        const deleteButton = getByRole(container, 'button');
        const link = getByText(container, container.textContent.trim());

        return {container, checkbox, deleteButton, link};
    });
}
Example #2
Source File: index.jsdom.test.jsx    From apps-todo-list with MIT License 6 votes vote down vote up
async function openAsync(table, view, field) {
    act(() => {
        const input = screen.getByLabelText('Table');
        const option = screen.getByText(table);

        userEvent.selectOptions(input, [option]);
    });

    act(() => {
        const input = screen.getByLabelText('View');
        const option = screen.getByText(view);

        userEvent.selectOptions(input, [option]);
    });

    act(() => {
        const input = screen.getByLabelText('Field');
        const option = screen.getByText(field);

        userEvent.selectOptions(input, [option]);
    });

    return waitFor(() => screen.getByRole('button', {name: 'Add'}));
}
Example #3
Source File: debugCart.test.js    From use-shopping-cart with MIT License 5 votes vote down vote up
describe('<DebugCart>', () => {
  beforeEach(() => {
    const Wrapper = createWrapper()
    act(() => {
      render(
        <Wrapper>
          <DebugCart />
        </Wrapper>
      )
      return undefined
    })
  })

  it('should make a table of properties and values from the cart', async () => {
    const { cartDetails, ...remainingState } = expectedInitialCartState
    // console.log(cartDetails)
    const tableElement = await screen.findByRole('table')
    expect(tableElement).toBeVisible()
    const cartDetailsCell = await screen.findByRole('cell', {
      name: 'cartDetails'
    })
    expect(cartDetailsCell).toBeVisible()

    const logButton = await findByRole(
      cartDetailsCell.parentElement,
      'button',
      { name: /log value/i }
    )
    expect(logButton).toBeVisible()

    for (const property in remainingState) {
      const keyCell = await screen.findByRole('cell', { name: property })
      const valueCell = await getByRole(keyCell.parentElement, 'cell', {
        name: JSON.stringify(remainingState[property])
      })

      expect(keyCell).toBeVisible()
      expect(valueCell).toBeVisible()
    }
  })
})
Example #4
Source File: BbbSettings.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
describe('BBB Settings', () => {
  beforeEach(async () => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: false,
        roles: [],
      },
    });
    store = initializeStore(initialState);
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());
    history.push(liveSettingsUrl);
  });

  test('Plan dropdown to be visible and enabled in UI', async () => {
    await mockStore({ emailSharing: true });
    renderComponent();

    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);

    expect(queryByTestId(container, 'plansDropDown')).toBeInTheDocument();
    expect(container.querySelector('select[name="tierType"]')).not.toBeDisabled();
  });

  it('Plan dropdown should display correct number of options', async () => {
    await mockStore({ emailSharing: true });
    renderComponent();
    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);
    const dropDown = queryByTestId(container, 'plansDropDown');
    expect(getAllByRole(dropDown, 'option').length).toBe(3);
  });

  test('Connect to support and PII sharing message is visible and plans selection is disabled, When pii sharing is disabled, ',
    async () => {
      await mockStore({ piiSharingAllowed: false });
      renderComponent();
      const spinner = getByRole(container, 'status');
      await waitForElementToBeRemoved(spinner);
      const requestPiiText = queryByTestId(container, 'request-pii-sharing');
      const helpRequestPiiText = queryByTestId(container, 'help-request-pii-sharing');
      expect(requestPiiText).toHaveTextContent(
        messages.requestPiiSharingEnableForBbb.defaultMessage.replaceAll('{provider}', 'BigBlueButton'),
      );
      expect(helpRequestPiiText).toHaveTextContent(messages.piiSharingEnableHelpTextBbb.defaultMessage);
      expect(container.querySelector('select[name="tierType"]')).toBeDisabled();
    });

  test('free plans message is visible when free plan is selected', async () => {
    await mockStore({ emailSharing: true, isFreeTier: true });
    renderComponent();
    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);
    const dropDown = container.querySelector('select[name="tierType"]');
    userEvent.selectOptions(
      dropDown,
     getByRole(dropDown, 'option', { name: 'Free' }),
    );
    expect(queryByTestId(container, 'free-plan-message')).toBeInTheDocument();
    expect(queryByTestId(container, 'free-plan-message')).toHaveTextContent(messages.freePlanMessage.defaultMessage);
  });
});
Example #5
Source File: FeatureList.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
describe('FeaturesList', () => {
  const app = {
    externalLinks: {},
    featureIds: ['discussion-page', 'embedded-course-sections', 'wcag-2.1'],
    hasFullSupport: false,
    id: 'legacy',
  };
  let container;

  beforeEach(() => {
    const wrapper = render(
      <IntlProvider locale="en">
        <FeaturesList
          app={app}
        />
      </IntlProvider>,
    );
    container = wrapper.container;
  });

  test('displays show app features message', () => {
    expect(queryByText(container, messages['supportedFeatureList-mobile-show'].defaultMessage)).toBeInTheDocument();
  });

  test('displays hide available feature message on expand', () => {
    const button = getByRole(container, 'button');
    userEvent.click(button);
    expect(queryByText(container, messages['supportedFeatureList-mobile-hide'].defaultMessage)).toBeInTheDocument();
  });

  test('displays a row for each available feature', () => {
    const button = getByRole(container, 'button');
    userEvent.click(button);
    app.featureIds.forEach((id) => {
      const featureNodes = queryAllByText(
        container, messages[`featureName-${id}`].defaultMessage,
      );
      expect(featureNodes.map(node => node.closest('div'))).toHaveLength(1);
    });
  });

  test('A check icon is shown with each supported feature', () => {
    const button = getByRole(container, 'button');
    userEvent.click(button);
    app.featureIds.forEach((id) => {
      const featureElement = queryByText(container, messages[`featureName-${id}`].defaultMessage);
      expect(featureElement.querySelector('svg')).toHaveAttribute('id', 'check-icon');
    });
  });
});
Example #6
Source File: AppList.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
describe('AppList', () => {
  let axiosMock;
  let store;
  let container;

  function createComponent(screenWidth = breakpoints.extraLarge.minWidth) {
    return (
      <AppProvider store={store}>
        <ResponsiveContext.Provider value={{ width: screenWidth }}>
          <IntlProvider locale="en" messages={{}}>
            <AppList />
          </IntlProvider>
        </ResponsiveContext.Provider>
      </AppProvider>
    );
  }

  beforeEach(async () => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: true,
        roles: [],
      },
    });

    store = await initializeStore();
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());
  });

  const mockStore = async (mockResponse, screenWidth = breakpoints.extraLarge.minWidth) => {
    axiosMock.onGet(getDiscussionsProvidersUrl(courseId)).reply(200, generateProvidersApiResponse());
    axiosMock.onGet(getDiscussionsSettingsUrl(courseId)).reply(200, mockResponse);
    await executeThunk(fetchProviders(courseId), store.dispatch);
    await executeThunk(fetchDiscussionSettings(courseId), store.dispatch);
    const component = createComponent(screenWidth);
    const wrapper = render(component);
    container = wrapper.container;
  };

  test('display a card for each available app', async () => {
    await mockStore(piazzaApiResponse);
    const appCount = store.getState().discussions.appIds.length;
    expect(queryAllByRole(container, 'radio')).toHaveLength(appCount);
  });

  test('displays the FeaturesTable at desktop sizes', async () => {
    await mockStore(piazzaApiResponse);
    expect(queryByRole(container, 'table')).toBeInTheDocument();
  });

  test('hides the FeaturesTable at mobile sizes', async () => {
    await mockStore(piazzaApiResponse, breakpoints.extraSmall.maxWidth);
    expect(queryByRole(container, 'table')).not.toBeInTheDocument();
  });

  test('hides the FeaturesList at desktop sizes', async () => {
    await mockStore(piazzaApiResponse);
    expect(queryByText(container, messages['supportedFeatureList-mobile-show'].defaultMessage)).not.toBeInTheDocument();
  });

  test('displays the FeaturesList at mobile sizes', async () => {
    await mockStore(piazzaApiResponse, breakpoints.extraSmall.maxWidth);
    const appCount = store.getState().discussions.appIds.length;
    expect(queryAllByText(container, messages['supportedFeatureList-mobile-show'].defaultMessage)).toHaveLength(appCount);
  });

  test('selectApp is called when an app is clicked', async () => {
    await mockStore(piazzaApiResponse);
    userEvent.click(getByLabelText(container, 'Select Piazza'));
    const clickedCard = getByRole(container, 'radio', { checked: true });
    expect(queryByLabelText(clickedCard, 'Select Piazza')).toBeInTheDocument();
  });
});
Example #7
Source File: DiscussionsSettings.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
describe.each([
  { piiSharingAllowed: false },
  { piiSharingAllowed: true },
])('PII sharing fields test', ({ piiSharingAllowed }) => {
  const enablePIISharing = false;

  beforeEach(() => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: true,
        roles: [],
      },
    });

    store = initializeStore({
      models: {
        courseDetails: {
          [courseId]: {},
        },
      },
    });
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());

    // Leave the DiscussionsSettings route after the test.
    history.push(`/course/${courseId}/pages-and-resources`);
    axiosMock.onGet(getDiscussionsProvidersUrl(courseId))
      .reply(200, generateProvidersApiResponse(false));
    axiosMock.onGet(getDiscussionsSettingsUrl(courseId))
      .reply(200, generatePiazzaApiResponse(piiSharingAllowed));
    renderComponent();
  });

  test(`${piiSharingAllowed ? 'shows PII share username/email field when piiSharingAllowed is true'
    : 'hides PII share username/email field when piiSharingAllowed is false'}`, async () => {
    history.push(`/course/${courseId}/pages-and-resources/discussion`);

    // This is an important line that ensures the spinner has been removed - and thus our main
    // content has been loaded - prior to proceeding with our expectations.
    await waitForElementToBeRemoved(screen.getByRole('status'));

    userEvent.click(queryByLabelText(container, 'Select Piazza'));
    userEvent.click(queryByText(container, messages.nextButton.defaultMessage));
    await waitForElementToBeRemoved(screen.getByRole('status'));
    if (enablePIISharing) {
      expect(queryByTestId(container, 'piiSharingFields')).toBeInTheDocument();
    } else {
      expect(queryByTestId(container, 'piiSharingFields')).not.toBeInTheDocument();
    }
  });
});
Example #8
Source File: DiscussionsSettings.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
describe.each([
  { isAdmin: false, isAdminOnlyConfig: false },
  { isAdmin: false, isAdminOnlyConfig: true },
  { isAdmin: true, isAdminOnlyConfig: false },
  { isAdmin: true, isAdminOnlyConfig: true },
])('LTI Admin only config test', ({ isAdmin, isAdminOnlyConfig }) => {
  beforeEach(() => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: isAdmin,
        roles: [],
      },
    });

    store = initializeStore({
      models: {
        courseDetails: {
          [courseId]: {},
        },
      },
    });
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());

    // Leave the DiscussionsSettings route after the test.
    history.push(`/course/${courseId}/pages-and-resources`);
    axiosMock.onGet(getDiscussionsProvidersUrl(courseId))
      .reply(200, generateProvidersApiResponse(isAdminOnlyConfig));
    axiosMock.onGet(getDiscussionsSettingsUrl(courseId))
      .reply(200, generatePiazzaApiResponse(true));
    renderComponent();
  });

  test(`successfully advances to settings step for lti when adminOnlyConfig=${isAdminOnlyConfig} and user ${isAdmin ? 'is' : 'is not'} admin `, async () => {
    const showLTIConfig = isAdmin;
    history.push(`/course/${courseId}/pages-and-resources/discussion`);

    // This is an important line that ensures the spinner has been removed - and thus our main
    // content has been loaded - prior to proceeding with our expectations.
    await waitForElementToBeRemoved(screen.getByRole('status'));

    userEvent.click(queryByLabelText(container, 'Select Piazza'));
    userEvent.click(queryByText(container, messages.nextButton.defaultMessage));
    await waitForElementToBeRemoved(screen.getByRole('status'));

    if (showLTIConfig) {
      expect(queryByText(container, ltiMessages.formInstructions.defaultMessage)).toBeInTheDocument();
      expect(queryByTestId(container, 'ltiConfigFields')).toBeInTheDocument();
    } else {
      expect(queryByText(container, ltiMessages.formInstructions.defaultMessage)).not.toBeInTheDocument();
      expect(queryByTestId(container, 'ltiConfigFields')).not.toBeInTheDocument();
    }
  });
});
Example #9
Source File: DnsTab.test.js    From cockpit-wicked with GNU General Public License v2.0 5 votes vote down vote up
describe('DnsTab', () => {
    const dnsSettings = model.createDnsSettings({
        policy: 'auto',
        nameServers: ['8.8.8.8'],
        searchList: ['suse.com']
    });
    const getDnsSettingsMock = jest.fn(() => Promise.resolve(dnsSettings));

    beforeAll(() => {
        resetClient();
        NetworkClient.mockImplementation(() => {
            return {
                getConnections: jest.fn(() => Promise.resolve([])),
                getInterfaces: jest.fn(() => Promise.resolve([])),
                getDnsSettings: getDnsSettingsMock,
                updateDnsSettings: jest.fn(),
                onInterfaceChange: jest.fn()
            };
        });
    });

    test('inspect DNS settings', async () => {
        act(() => {
            customRender(<DnsTab />, {});
        });

        expect(await screen.findByText('auto')).toBeInTheDocument();
        expect(screen.getByRole('link', { name: 'auto' })).toBeInTheDocument();
        expect(screen.getByRole('link', { name: '8.8.8.8' })).toBeInTheDocument();
        expect(screen.getByRole('link', { name: 'suse.com' })).toBeInTheDocument();
    });

    test('change DNS settings', async () => {
        act(() => {
            customRender(<DnsTab />, {});
        });

        expect(await screen.findByText('auto')).toBeInTheDocument();
        userEvent.click(screen.getByRole('link', { name: 'auto' }));
        const dialog = await screen.findByRole('dialog');
        userEvent.type(getByLabelText(dialog, /search/i), ' foo.bar');
        userEvent.click(getByRole(dialog, 'button', { name: 'Apply' }));
        expect(await screen.findByText('suse.com foo.bar')).toBeInTheDocument();
    });
});
Example #10
Source File: ZoomSettings.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('Zoom Settings', () => {
  beforeEach(async () => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: false,
        roles: [],
      },
    });
    store = initializeStore(initialState);
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());
    history.push(liveSettingsUrl);
  });

  test('LTI fields are visible when pii sharing is enabled and email or username sharing required', async () => {
    await mockStore({ emailSharing: true });
    renderComponent();

    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);

    const consumerKey = container.querySelector('input[name="consumerKey"]').parentElement;
    const consumerSecret = container.querySelector('input[name="consumerSecret"]').parentElement;
    const launchUrl = container.querySelector('input[name="launchUrl"]').parentElement;
    const launchEmail = container.querySelector('input[name="launchEmail"]').parentElement;

    expect(consumerKey.firstChild).toBeVisible();
    expect(consumerKey.lastChild).toHaveTextContent(messages.consumerKey.defaultMessage);
    expect(consumerSecret.firstChild).toBeVisible();
    expect(consumerSecret.lastChild).toHaveTextContent(messages.consumerSecret.defaultMessage);
    expect(launchUrl.firstChild).toBeVisible();
    expect(launchUrl.lastChild).toHaveTextContent(messages.launchUrl.defaultMessage);
    expect(launchEmail.firstChild).toBeVisible();
    expect(launchEmail.lastChild).toHaveTextContent(messages.launchEmail.defaultMessage);
  });

  test(
    'Only connect to support message is visible when pii sharing is disabled and email or username sharing is required',
    async () => {
      await mockStore({ emailSharing: true, piiSharingAllowed: false });
      renderComponent();

      const spinner = getByRole(container, 'status');
      await waitForElementToBeRemoved(spinner);

      const requestPiiText = queryByTestId(container, 'request-pii-sharing');
      const consumerKey = container.querySelector('input[name="consumerKey"]');
      const consumerSecret = container.querySelector('input[name="consumerSecret"]');
      const launchUrl = container.querySelector('input[name="launchUrl"]');
      const launchEmail = container.querySelector('input[name="launchEmail"]');

      expect(requestPiiText).toHaveTextContent(
        messages.requestPiiSharingEnable.defaultMessage.replaceAll('{provider}', 'Zoom'),
      );
      expect(consumerKey).not.toBeInTheDocument();
      expect(consumerSecret).not.toBeInTheDocument();
      expect(launchUrl).not.toBeInTheDocument();
      expect(launchEmail).not.toBeInTheDocument();
    },
  );

  test('Provider Configuration should be displayed correctly', async () => {
    const apiDefaultResponse = generateLiveConfigurationApiResponse(true, true);
    await mockStore({ emailSharing: false, piiSharingAllowed: false });
    renderComponent();

    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);

    const consumerKey = container.querySelector('input[name="consumerKey"]');
    const consumerSecret = container.querySelector('input[name="consumerSecret"]');
    const launchUrl = container.querySelector('input[name="launchUrl"]');
    const launchEmail = container.querySelector('input[name="launchEmail"]');

    expect(consumerKey.value).toBe(apiDefaultResponse.lti_configuration.lti_1p1_client_key);
    expect(consumerSecret.value).toBe(apiDefaultResponse.lti_configuration.lti_1p1_client_secret);
    expect(launchUrl.value).toBe(apiDefaultResponse.lti_configuration.lti_1p1_launch_url);
    expect(launchEmail.value).toBe(
      apiDefaultResponse.lti_configuration.lti_config.additional_parameters.custom_instructor_email,
    );
  });
});
Example #11
Source File: Settings.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('LiveSettings', () => {
  beforeEach(async () => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: false,
        roles: [],
      },
    });
    store = initializeStore(initialState);
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());
    history.push(liveSettingsUrl);
  });

  test('Live Configuration modal is visible', async () => {
    renderComponent();
    expect(queryByRole(container, 'dialog')).toBeVisible();
  });

  test('Displays "Configure Live" heading', async () => {
    renderComponent();
    const headingElement = queryByTestId(container, 'modal-title');
    expect(headingElement).toHaveTextContent(messages.heading.defaultMessage);
  });

  test('Displays title, helper text and badge when live configuration button is enabled', async () => {
    await mockStore({ enabled: true });
    renderComponent();

    const label = container.querySelector('label[for="enable-live-toggle"]');
    const helperText = container.querySelector('#enable-live-toggleHelpText');
    const enableBadge = queryByTestId(container, 'enable-badge');

    expect(label).toHaveTextContent(messages.enableLiveLabel.defaultMessage);
    expect(enableBadge).toHaveTextContent('Enabled');
    expect(helperText).toHaveTextContent(messages.enableLiveHelp.defaultMessage);
  });

  test('Displays title, helper text and hides badge when live configuration button is disabled', async () => {
    await mockStore({ enabled: false, piiSharingAllowed: false });
    renderComponent();

    const label = container.querySelector('label[for="enable-live-toggle"]');
    const helperText = container.querySelector('#enable-live-toggleHelpText');

    expect(label).toHaveTextContent('Live');
    expect(label.firstChild).not.toHaveTextContent('Enabled');
    expect(helperText).toHaveTextContent(messages.enableLiveHelp.defaultMessage);
  });

  test('Displays provider heading, helper text and all providers', async () => {
    await mockStore({
      enabled: true,
      piiSharingAllowed: true,
      usernameSharing: false,
      emailSharing: true,
    });
    renderComponent();

    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);
    const providers = queryByRole(container, 'group');
    const helperText = queryByTestId(container, 'helper-text');

    expect(providers.childElementCount).toBe(2);
    expect(providers).toHaveTextContent('Zoom');
    expect(providers).toHaveTextContent('BigBlueButton');
    expect(helperText).toHaveTextContent(
      messages.providerHelperText.defaultMessage.replace('{providerName}', 'Zoom'),
    );
  });

  test('Unable to save error should be shown on submission if a field is empty', async () => {
    const apiDefaultResponse = generateLiveConfigurationApiResponse(true, true);
    apiDefaultResponse.lti_configuration.lti_1p1_client_key = '';
    await mockStore({ emailSharing: false, piiSharingAllowed: false });
    renderComponent();

    const spinner = getByRole(container, 'status');
    await waitForElementToBeRemoved(spinner);

    const saveButton = queryByText(container, 'Save');

    await waitFor(async () => {
      await act(async () => fireEvent.click(saveButton));
      expect(queryByRole(container, 'alert')).toBeVisible();
    });
  });
});
Example #12
Source File: DiscussionsSettings.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('DiscussionsSettings', () => {
  beforeEach(() => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: true,
        roles: [],
      },
    });

    store = initializeStore({
      models: {
        courseDetails: {
          [courseId]: {
            start: Date(),
          },
        },
      },
    });
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());

    // Leave the DiscussionsSettings route after the test.
    history.push(`/course/${courseId}/pages-and-resources`);
  });

  describe('with successful network connections', () => {
    beforeEach(() => {
      axiosMock.onGet(getDiscussionsProvidersUrl(courseId))
        .reply(200, generateProvidersApiResponse(false));
      axiosMock.onGet(getDiscussionsSettingsUrl(courseId))
        .reply(200, generatePiazzaApiResponse(true));
      renderComponent();
    });

    test('sets selection step from routes', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      expect(queryByTestId(container, 'appList')).toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument();
    });

    test('sets settings step from routes', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      expect(queryByTestId(container, 'appList')).not.toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument();
    });

    test('successfully advances to settings step for lti', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      userEvent.click(queryByLabelText(container, 'Select Piazza'));
      userEvent.click(queryByText(container, messages.nextButton.defaultMessage));

      await waitForElementToBeRemoved(screen.getByRole('status'));

      expect(queryByTestId(container, 'appList')).not.toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument();
      expect(queryByTestId(container, 'ltiConfigForm')).toBeInTheDocument();
      expect(queryByTestId(container, 'legacyConfigForm')).not.toBeInTheDocument();
    });

    test('successfully advances to settings step for legacy', async () => {
      axiosMock.onGet(getDiscussionsProvidersUrl(courseId)).reply(200, generateProvidersApiResponse(false, 'legacy'));
      axiosMock.onGet(getDiscussionsSettingsUrl(courseId)).reply(200, legacyApiResponse);
      renderComponent();
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      userEvent.click(queryByLabelText(container, 'Select edX'));
      userEvent.click(queryByText(container, messages.nextButton.defaultMessage));

      await waitForElementToBeRemoved(screen.getByRole('status'));

      expect(queryByTestId(container, 'appList')).not.toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument();
      expect(queryByTestId(container, 'ltiConfigForm')).not.toBeInTheDocument();
      expect(queryByTestId(container, 'legacyConfigForm')).toBeInTheDocument();
    });

    test('successfully goes back to first step', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument();

      userEvent.click(queryByText(container, appMessages.backButton.defaultMessage));

      expect(queryByTestId(container, 'appList')).toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument();
    });

    test('successfully closes the modal', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      expect(queryByTestId(container, 'appList')).toBeInTheDocument();

      userEvent.click(queryByLabelText(container, 'Close'));

      expect(queryByTestId(container, 'appList')).not.toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument();

      expect(window.location.pathname).toEqual(`/course/${courseId}/pages-and-resources`);
    });

    test('successfully submit the modal', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      axiosMock.onPost(getDiscussionsSettingsUrl(courseId)).reply(200, generatePiazzaApiResponse(true));

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      userEvent.click(queryByLabelText(container, 'Select Piazza'));

      userEvent.click(getByRole(container, 'button', { name: 'Next' }));

      userEvent.click(await findByRole(container, 'button', { name: 'Save' }));

      // This is an important line that ensures the Close button has been removed, which implies that
      // the full screen modal has been closed following our click of Apply.  Once this has happened,
      // then it's safe to proceed with our expectations.
      await waitForElementToBeRemoved(queryByRole(container, 'button', { name: 'Close' }));

      await waitFor(() => expect(window.location.pathname).toEqual(`/course/${courseId}/pages-and-resources`));
    });

    test('requires confirmation if changing provider', async () => {
      axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/courses/v1/courses/${courseId}?username=abc123`).reply(200, courseDetailResponse);
      await executeThunk(fetchCourseDetail(courseId), store.dispatch);
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      userEvent.click(getByRole(container, 'checkbox', { name: 'Select Discourse' }));
      userEvent.click(getByRole(container, 'button', { name: 'Next' }));

      await findByRole(container, 'button', { name: 'Save' });
      userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Key' }), 'key');
      userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Secret' }), 'secret');
      userEvent.type(getByRole(container, 'textbox', { name: 'Launch URL' }), 'http://example.test');
      userEvent.click(getByRole(container, 'button', { name: 'Save' }));

      await waitFor(() => expect(getByRole(container, 'dialog', { name: 'OK' })).toBeInTheDocument());
    });

    test('can cancel confirmation', async () => {
      axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/courses/v1/courses/${courseId}?username=abc123`).reply(200, courseDetailResponse);
      await executeThunk(fetchCourseDetail(courseId), store.dispatch);
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      const discourseBox = getByRole(container, 'checkbox', { name: 'Select Discourse' });
      expect(discourseBox).not.toBeDisabled();
      userEvent.click(discourseBox);

      userEvent.click(getByRole(container, 'button', { name: 'Next' }));
      await waitForElementToBeRemoved(screen.getByRole('status'));
      expect(getByRole(container, 'heading', { name: 'Discourse' })).toBeInTheDocument();

      userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Key' }), 'a');
      userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Secret' }), 'secret');
      userEvent.type(getByRole(container, 'textbox', { name: 'Launch URL' }), 'http://example.test');
      userEvent.click(getByRole(container, 'button', { name: 'Save' }));

      await waitFor(() => expect(getByRole(container, 'dialog', { name: 'OK' })).toBeInTheDocument());
      userEvent.click(getByRole(container, 'button', { name: 'Cancel' }));

      expect(queryByRole(container, 'dialog', { name: 'Confirm' })).not.toBeInTheDocument();
      expect(queryByRole(container, 'dialog', { name: 'Configure discussion' }));
    });
  });

  describe('with network error fetchProviders API requests', () => {
    beforeEach(() => {
      // Expedient way of getting SUPPORT_URL into config.
      setConfig({
        ...getConfig(),
        SUPPORT_URL: 'http://support.edx.org',
      });

      axiosMock.onGet(getDiscussionsProvidersUrl(courseId)).networkError();
      axiosMock.onGet(getDiscussionsSettingsUrl(courseId)).networkError();
      renderComponent();
    });

    test('shows connection error alert', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      const alert = queryByRole(container, 'alert');
      expect(alert).toBeInTheDocument();
      expect(alert.textContent).toEqual(expect.stringContaining('We encountered a technical error when loading this page.'));
      expect(alert.innerHTML).toEqual(expect.stringContaining(getConfig().SUPPORT_URL));
    });
  });

  describe('with network error postAppConfig API requests', () => {
    beforeEach(() => {
      // Expedient way of getting SUPPORT_URL into config.
      setConfig({
        ...getConfig(),
        SUPPORT_URL: 'http://support.edx.org',
      });

      axiosMock.onGet(getDiscussionsProvidersUrl(courseId))
        .reply(200, generateProvidersApiResponse());
      axiosMock.onGet(getDiscussionsSettingsUrl(courseId))
        .reply(200, piazzaApiResponse);
      axiosMock.onPost(getDiscussionsSettingsUrl(courseId)).networkError();
      renderComponent();
    });

    test('shows connection error alert at top of form', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      // Apply causes an async action to take place
      act(() => {
        userEvent.click(queryByText(container, appMessages.saveButton.defaultMessage));
      });

      await waitFor(() => expect(axiosMock.history.post.length).toBe(1));

      expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument();
      const alert = await findByRole(container, 'alert');
      expect(alert).toBeInTheDocument();
      expect(alert.textContent).toEqual(expect.stringContaining('We encountered a technical error when applying changes.'));
      expect(alert.innerHTML).toEqual(expect.stringContaining(getConfig().SUPPORT_URL));
    });
  });

  describe('with permission denied error for fetchProviders API requests', () => {
    beforeEach(() => {
      axiosMock.onGet(getDiscussionsProvidersUrl(courseId)).reply(403);
      axiosMock.onGet(getDiscussionsSettingsUrl(courseId)).reply(403);

      renderComponent();
    });

    test('shows permission denied alert', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      const alert = queryByRole(container, 'alert');
      expect(alert).toBeInTheDocument();
      expect(alert.textContent).toEqual(expect.stringContaining('You are not authorized to view this page.'));
    });
  });

  describe('with permission denied error for postAppConfig API requests', () => {
    beforeEach(() => {
      axiosMock.onGet(getDiscussionsProvidersUrl(courseId))
        .reply(200, generateProvidersApiResponse());
      axiosMock.onGet(getDiscussionsSettingsUrl(courseId)).reply(200, piazzaApiResponse);
      axiosMock.onPost(getDiscussionsSettingsUrl(courseId)).reply(403);

      renderComponent();
    });

    test('shows permission denied alert at top of form', async () => {
      history.push(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`);

      // This is an important line that ensures the spinner has been removed - and thus our main
      // content has been loaded - prior to proceeding with our expectations.
      await waitForElementToBeRemoved(screen.getByRole('status'));

      userEvent.click(getByRole(container, 'button', { name: 'Save' }));

      await waitFor(() => expect(axiosMock.history.post.length).toBe(1));

      expect(queryByTestId(container, 'appList')).not.toBeInTheDocument();
      expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument();

      // We don't technically leave the route in this case, though the modal is hidden.
      expect(window.location.pathname).toEqual(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`);

      const alert = await findByRole(container, 'alert');
      expect(alert).toBeInTheDocument();
      expect(alert.textContent).toEqual(expect.stringContaining('You are not authorized to view this page.'));
    });
  });
});
Example #13
Source File: edit-schedule.test.js    From what-front with MIT License 4 votes vote down vote up
describe('Tests EditSchedule', () => {
  describe('Render && Form of EditSchedule', () => {
    let historyMock;

    beforeEach(() => {
      useSelector
        .mockReturnValue(scheduleMockDataLoading)
        .mockReturnValue(scheduleMockData)
        .mockReturnValue(studentGroupMockDataLoading)
        .mockReturnValue(studentGroupMockData)
        .mockReturnValue(mentorsMockData)
        .mockReturnValue(themesMockData);

      commonHelpers.transformDateTime = jest
        .fn()
        .mockReturnValue({ formInitialValue: '2021-06-17T02:47' });

      historyMock = { push: jest.fn(), location: jest.fn(), listen: jest.fn() };
    });
    afterEach(cleanup);

    it('should loader appear when scheduleSelector .isLoading is true', () => {
      const mockedScheduleSelector = {
        data: scheduleMockDataLoading,
        isLoading: true,
      };
      useSelector.mockReturnValue(mockedScheduleSelector);
      const { container } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={mockedScheduleSelector}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const loader = container.querySelector('.spinner-border');
      expect(loader).toBeInTheDocument();
    });

    it('should loader appear when groupSelector .isLoading is true', () => {
      const mockedGroupSelector = {
        data: studentGroupMockDataLoading,
        isLoading: true,
      };
      useSelector.mockReturnValue(mockedGroupSelector);
      const { container } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={mockedGroupSelector}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const loader = container.querySelector('.spinner-border');
      expect(loader).toBeInTheDocument();
    });

    it('should the component be rendered', () => {
      const { getByTestId } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      expect(getByTestId('editForm')).toBeInTheDocument();
    });

    it('should the Form be rendered correctly', () => {
      const { getByTestId } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const groupName = getByTestId('groupName');
      const eventStart = getByTestId('eventStart');
      const eventFinish = getByTestId('eventFinish');
      expect(getByTestId('editForm')).toBeInTheDocument();
      expect(groupName.value).toBe('122-18-3');
      expect(eventStart.value).toBe('2021-06-17T02:47');
      expect(eventFinish.value).toBe('2021-06-17T02:47');
      expect(groupName).toBeDisabled();
    });

    it('should weekDays list = 7 ', () => {
      const { container } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const weekDaysList = container.querySelector('#weekDays-list');
      fireEvent.click(weekDaysList);
      expect(weekDaysList.children.length).toEqual(7);
      expect(weekDaysList.children[0].value).toBe('Monday');
    });

    it('should day of week be chosen', () => {
      const { getByPlaceholderText } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const weekDay = getByPlaceholderText("Select day('s) of week");
      fireEvent.change(weekDay, {
        target: { value: helpersData.daysOfWeek[3].name },
      });
      expect(weekDay.value).toBe('Thursday');
    });
  });

  describe('Redirect correctly', () => {
    let historyMock;
    let data;
    let mockEditedScheduleSelector;
    let mockDeletedScheduleSelector;

    beforeEach(() => {
      data = scheduleMockData;
      data.isLoading = false;

      mockEditedScheduleSelector = {
        isLoading: false,
        isLoaded: true,
        error: '',
      };
      mockDeletedScheduleSelector = {
        isLoading: false,
        isLoaded: true,
        error: '',
      };

      useSelector
        .mockReturnValue(mockEditedScheduleSelector)
        .mockReturnValue(mockDeletedScheduleSelector)
        .mockReturnValue(scheduleMockData)
        .mockReturnValue(studentGroupMockData)
        .mockReturnValue(mentorsMockData)
        .mockReturnValue(themesMockData);

      historyMock = { push: jest.fn(), location: jest.fn(), listen: jest.fn() };
    });
    afterEach(cleanup);

    it('should redirrect to page 404 in case of error', () => {
      const noData = {
        data: '',
        isLoading: true,
        isLoaded: true,
      };
      useSelector.mockReturnValue(noData);
      render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={noData}
            groupData={noData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      expect(historyMock.push).toHaveBeenCalledWith(paths.NOT_FOUND);
    });

    it('should redirrect to schedule page and show success alert after successful editing', () => {
      mockEditedScheduleSelector = {
        ...data,
        isLoaded: true,
      };
      useSelector
        .mockReturnValueOnce(mockEditedScheduleSelector)
        .mockReturnValueOnce(mockDeletedScheduleSelector);

      render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );

      expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
        'The schedule has been successfully edited',
        'success'
      );
    });

    it('should show error alert after unsuccessful editing', () => {
      mockEditedScheduleSelector = {
        ...data,
        isLoaded: false,
        error: 'some error',
      };
      useSelector
        .mockReturnValueOnce(mockEditedScheduleSelector)
        .mockReturnValueOnce(mockDeletedScheduleSelector);

      render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );

      expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
        mockEditedScheduleSelector.error
      );
    });

    it('should redirrect to schedule page and show success alert after successful deleting', () => {
      mockDeletedScheduleSelector = {
        ...data,
        isLoaded: true,
      };
      useSelector
        .mockReturnValueOnce(mockEditedScheduleSelector)
        .mockReturnValueOnce(mockDeletedScheduleSelector);

      render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );

      expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
        'The schedule has been successfully deleted',
        'success'
      );
    });

    it('should show error alert after unsuccessful deleting', () => {
      mockDeletedScheduleSelector = {
        ...data,
        isLoaded: false,
        error: 'some error',
      };
      useSelector
        .mockReturnValueOnce(mockEditedScheduleSelector)
        .mockReturnValueOnce(mockDeletedScheduleSelector);

      render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );

      expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
        mockDeletedScheduleSelector.error
      );
    });

    it('should open modalWindow when click delete', async () => {
      const { getByText } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const handleShowModal = jest.fn();
      const removeBtn = getByText(/Delete/i);
      await waitFor(() => {
        fireEvent.click(removeBtn);
        handleShowModal();
      });
      expect(handleShowModal).toHaveBeenCalledTimes(1);
      expect(
        getByText(/Are you sure you want to exclude this schedule?/i)
      ).toBeInTheDocument();
    });

    it('should close modalWindow and delete course when click delete on modalWindow', async () => {
      const { getByText, getByRole } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const handleDelete = jest.fn();
      const removeBtn = getByText('Delete');

      await waitFor(() => {
        fireEvent.click(removeBtn);
      });
      expect(getByRole('button', { name: 'Delete' })).toBeInTheDocument();
      expect(getByRole('button', { name: 'Cancel' })).toBeInTheDocument();

      const deleteBtn = getByRole('button', { name: 'Delete' });
      await waitFor(() => {
        fireEvent.click(deleteBtn);
        handleDelete();
      });
      expect(handleDelete).toHaveBeenCalled();
      expect(useActionsFns.removeSchedule).toHaveBeenCalled();
    });

    it('should day of week not Found', () => {
      React.useState = jest
        .fn()
        .mockReturnValue(['', useStates.dayInputError.setDayInputError]);
      const { getByPlaceholderText } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const weekDay = getByPlaceholderText("Select day('s) of week");
      fireEvent.change(weekDay, { target: { value: 'day' } });
      expect(useStates.dayInputError.setDayInputError).toHaveBeenCalled();
    });

    it('should call addDayOfWeek and change state', async () => {
      React.useState = jest
        .fn()
        .mockReturnValue([[], useStates.weekDays.setWeekDays]);
      const { container, getByPlaceholderText, getByText } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const addDayOfWeek = jest.fn();
      const clearField = jest.fn();
      const addDayOfWeekBtn = container.querySelector('#add-weekDay-btn');
      const weekDay = getByPlaceholderText("Select day('s) of week");

      await waitFor(() => {
        fireEvent.change(weekDay, {
          target: { value: helpersData.daysOfWeek[3].name },
        });
      });
      await waitFor(() => {
        fireEvent.click(addDayOfWeekBtn);
        addDayOfWeek();
        clearField();
      });
      expect(addDayOfWeek).toHaveBeenCalledTimes(1);
      expect(useStates.weekDays.setWeekDays).toHaveBeenCalled();
      expect(getByText(/Thursday/i)).toBeInTheDocument();
    });

    it('should call removeDayOfWeek and change state', async () => {
      React.useState = jest
        .fn()
        .mockReturnValue([[], useStates.weekDays.setWeekDays]);
      const { container, getByPlaceholderText } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const addDayOfWeek = jest.fn();
      const removeDayOfWeek = jest.fn();
      const addDayOfWeekBtn = container.querySelector('#add-weekDay-btn');
      const weekDay = getByPlaceholderText("Select day('s) of week");

      await waitFor(() => {
        fireEvent.change(weekDay, {
          target: { value: helpersData.daysOfWeek[3].name },
        });
      });
      await waitFor(() => {
        fireEvent.click(addDayOfWeekBtn);
        addDayOfWeek();
      });

      const dayContainer = container.querySelector('#chosenWeekDay');
      const removeBtn = dayContainer.children[0];

      expect(removeBtn).toHaveTextContent('X');

      await waitFor(() => {
        fireEvent.click(removeBtn);
        removeDayOfWeek();
      });

      expect(removeDayOfWeek).toHaveBeenCalledTimes(1);
      expect(useStates.weekDays.setWeekDays).toHaveBeenCalled();
    });

    it('should change interval', async () => {
      React.useState = jest
        .fn()
        .mockReturnValue([1, useStates.interval.setInterval]);
      const { getByTestId } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );

      const intervalInput = getByTestId('interval');
      await waitFor(() => {
        fireEvent.change(intervalInput, { target: { value: 3 } });
      });
      expect(useStates.interval.setInterval).toHaveBeenCalled();
      expect(intervalInput.value).toBe('3');
    });

    it('should handleReset', async () => {
      React.useState = jest
        .fn()
        .mockReturnValue([1, useStates.interval.setInterval]);
      const { container, getByTestId, getByRole, getByPlaceholderText } = render(
        <Router history={historyMock}>
          <EditSchedule
            id={1}
            schedulesData={scheduleMockData}
            groupData={studentGroupMockData}
            themesData={themesMockData}
            mentorsData={mentorsMockData}
          />
        </Router>
      );
      const handleReset = jest.fn();
      const setInterval = jest.fn();
      const setWeekDays = jest.fn();
      const intervalInput = getByTestId('interval');
      const cancleBtn = getByRole('button', { name: 'Clear' });
     const addDayOfWeekBtn = container.querySelector('#add-weekDay-btn');
      const weekDay = getByPlaceholderText("Select day('s) of week");

      await waitFor(() => {
        fireEvent.change(intervalInput, { target: { value: 3 } });
        fireEvent.change(weekDay, {
          target: { value: helpersData.daysOfWeek[3].name },
        });
        fireEvent.click(addDayOfWeekBtn);
      });

      expect(intervalInput.value).toBe('3');

      await waitFor(() => {
        fireEvent.click(cancleBtn);
        handleReset();
        setInterval(1);
        setWeekDays([]);
      });

      expect(handleReset).toHaveBeenCalled();
      expect(setInterval).toHaveBeenCalled();
      expect(setWeekDays).toHaveBeenCalled();
      expect(useStates.interval.setInterval).toHaveBeenCalled();
      expect(intervalInput.value).toBe('1');
    });

  });
});
Example #14
Source File: MappingProfilesTransformationsModal.test.js    From ui-data-export with Apache License 2.0 4 votes vote down vote up
describe('MappingProfilesTransformationsModal', () => {
  describe('rendering mapping profile transformations modal', () => {
    const onSubmitMock = jest.fn();
    let modal;

    beforeEach(() => {
      renderWithIntl(
        <MappingProfilesTransformationModalContainer
          onSubmit={onSubmitMock}
        />,
        translationsProperties
      );
      modal = document.querySelector('[data-test-transformations-modal]');
    });

    afterEach(() => {
      jest.clearAllMocks();
    });

    it('should display transformations modal', () => {
      expect(modal).toBeInTheDocument();
    });

    it('should display search pane', () => {
      expect(modal.querySelector('[data-test-transformations-search-pane]')).toBeInTheDocument();
    });

    it('should display results pane', () => {
      const resultsPane = modal.querySelector('[data-test-transformations-results-pane]');

      expect(resultsPane).toBeInTheDocument();
      expect(getByText(resultsPane, 'Transformations')).toBeInTheDocument();
      expect(getByText(resultsPane, `${initialValues.transformations.length} fields found`)).toBeInTheDocument();
    });

    it('should display total selected count', () => {
      expect(getByText(modal, 'Total selected: 0')).toBeInTheDocument();
    });

    it('should display search form', () => {
      expect(modal.querySelector('[data-test-transformations-search-form]')).toBeInTheDocument();
    });

    it('should display correct folio record types', () => {
      const checkboxes = getAllByRole(modal, 'checkbox');

      expect(checkboxes[0].value).toBe('INSTANCE');
      expect(checkboxes[1].value).toBe('HOLDINGS');
      expect(checkboxes[2].value).toBe('ITEM');
    });

    it('should display cancel button', () => {
      expect(getByRole(modal, 'button', { name: 'Cancel' })).toBeInTheDocument();
    });

    it('should display save button', () => {
      expect(getByRole(modal, 'button', { name: 'Save & close' })).toBeInTheDocument();
    });

    describe('filling transformation values', () => {
      let transformationFields;
      let submitButton;

      beforeEach(() => {
        transformationFields = getTransformationFieldGroups();
        submitButton = getByRole(modal, 'button', { name: 'Save & close' });
        userEvent.type(transformationFields[0].marcField.input, '123');
        userEvent.type(transformationFields[0].indicator1.input, '1');
        userEvent.type(transformationFields[0].indicator2.input, '0');
        userEvent.type(transformationFields[0].subfield.input, '$r');
      });

      it('should enable submit button', () => {
        expect(submitButton).not.toBeDisabled();
      });

      it('should validate fields as valid and initiate modal submit', () => {
        userEvent.click(submitButton);

        expect(onSubmitMock).toBeCalled();
      });

      describe('selecting all fields and clicking submit', () => {
        beforeEach(() => {
          userEvent.click(screen.getByLabelText('Select all fields'));
          userEvent.click(submitButton);
          transformationFields = getTransformationFieldGroups();
        });

        it('should not initiate modal submit', () => {
          expect(onSubmitMock).not.toBeCalled();
        });

        it('should disable submit button', () => {
          expect(submitButton).toBeDisabled();
        });

        it('should validate instance record type transformation as valid (instances can be empty)', () => {
          expect(transformationFields[1].isInvalid).toBe(false);
        });

        it('should validate item record type transformation and mark marcField as invalid', () => {
          expect(transformationFields[2].isInvalid).toBe(true);
          expect(transformationFields[2].marcField.isInvalid).toBe(true);
          expect(transformationFields[2].indicator1.isInvalid).toBe(false);
          expect(transformationFields[2].indicator2.isInvalid).toBe(false);
          expect(transformationFields[2].subfield.isInvalid).toBe(false);
        });

        describe('filling empty invalid field with invalid transformation', () => {
          beforeEach(() => {
            userEvent.type(transformationFields[2].subfield.input, '12');
            userEvent.click(submitButton);
            transformationFields = getTransformationFieldGroups();
          });

          it('should mark transformation field group, marc field and subfield as invalid', () => {
            expect(transformationFields[2].isInvalid).toBe(true);
            expect(transformationFields[2].marcField.isInvalid).toBe(true);
            expect(transformationFields[2].indicator1.isInvalid).toBe(false);
            expect(transformationFields[2].indicator2.isInvalid).toBe(false);
            expect(transformationFields[2].subfield.isInvalid).toBe(true);
          });

          describe('changing marc field', () => {
            beforeEach(() => {
              userEvent.type(transformationFields[2].marcField.input, '900');
              transformationFields = getTransformationFieldGroups();
            });

            it('should mark marc field as valid and keep subfield and transformation group as invalid', () => {
              expect(transformationFields[2].isInvalid).toBe(true);
              expect(transformationFields[2].marcField.isInvalid).toBe(false);
              expect(transformationFields[2].subfield.isInvalid).toBe(true);
            });

            it('should mark subfield as valid as well as transformation group after addressing every invalid field', () => {
              userEvent.type(transformationFields[2].subfield.input, '$12');
              transformationFields = getTransformationFieldGroups();

              expect(transformationFields[2].isInvalid).toBe(false);
              expect(transformationFields[2].subfield.isInvalid).toBe(false);
            });
          });
        });

        describe('filling empty invalid field with valid transformation', () => {
          beforeEach(() => {
            userEvent.type(transformationFields[2].marcField.input, '900');
            userEvent.type(transformationFields[2].indicator1.input, 'r');
            userEvent.type(transformationFields[2].indicator2.input, '1');
            userEvent.type(transformationFields[2].subfield.input, '$90');
            userEvent.click(submitButton);
          });

          it('should validate fields as valid and initiate modal submit', () => {
            transformationFields = getTransformationFieldGroups();
            expect(transformationFields[2].marcField.isInvalid).toBe(false);
            expect(transformationFields[2].indicator1.isInvalid).toBe(false);
            expect(transformationFields[2].indicator2.isInvalid).toBe(false);
            expect(transformationFields[2].subfield.isInvalid).toBe(false);
            expect(onSubmitMock).toBeCalledWith([{
              enabled: true,
              fieldId: 'field1',
              rawTransformation: {
                marcField: '123',
                indicator1: '1',
                indicator2: '0',
                subfield: '$r',
              },
              recordType: 'HOLDINGS',
              transformation: '12310$r',
            }, {
              enabled: true,
              fieldId: 'field2',
              recordType: 'INSTANCE',
              transformation: '',
            }, {
              enabled: true,
              fieldId: 'field3',
              rawTransformation: {
                marcField: '900',
                indicator1: 'r',
                indicator2: '1',
                subfield: '$90',
              },
              recordType: 'ITEM',
              transformation: '900r1$90',
            }]);
          });
        });
      });
    });
  });
});
Example #15
Source File: MappingProfilesFormContainer.test.js    From ui-data-export with Apache License 2.0 4 votes vote down vote up
describe('MappingProfileFormContainer', () => {
  describe('rendering MappingProfileForm', () => {
    const onSubmitMock = jest.fn();

    beforeEach(() => {
      renderWithIntl(
        <MappingProfileFormContainer
          allTransformations={allMappingProfilesTransformations}
          onSubmit={onSubmitMock}
        />,
        translationsProperties
      );
    });

    it('should place accordion headers in correct order', () => {
      const headers = document.querySelectorAll('[data-test-headline]');

      expect(getByText(headers[0], 'Summary')).toBeVisible();
      expect(getByText(headers[1], 'Transformations')).toBeVisible();
    });

    it('should place summary fields in correct order', () => {
      const labels = document.querySelectorAll('[data-test-accordion-section] label');

      expect(getByText(labels[0], 'Name')).toBeVisible();
      expect(getByText(labels[1], 'Output format')).toBeVisible();
      expect(getByText(labels[2], 'FOLIO record type')).toBeVisible();
      expect(getByText(labels[3], 'Source record storage (entire record)')).toBeVisible();
      expect(getByText(labels[4], 'Inventory instance (selected fields)')).toBeVisible();
      expect(getByText(labels[5], 'Holdings')).toBeVisible();
      expect(getByText(labels[6], 'Item')).toBeVisible();
      expect(getByText(labels[7], 'Description')).toBeVisible();
    });

    it('should disable instance record type option when selecting SRS', () => {
      userEvent.click(screen.getByRole('checkbox', { name: 'Source record storage (entire record)' }));

      expect(screen.getByDisplayValue('INSTANCE')).toBeDisabled();
    });

    it('should disable instance record type option in transformation list when selecting SRS', () => {
      userEvent.click(screen.getByRole('checkbox', { name: 'Source record storage (entire record)' }));
      userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
      const modalRecordFilters = screen.getByRole('region', { name: 'Record type filter list' });

      expect(getByRole(modalRecordFilters, 'checkbox', { name: 'Instance' })).toBeDisabled();
    });

    it('should disable SRS record type option when selecting instance', () => {
      userEvent.click(screen.getByRole('checkbox', { name: 'Inventory instance (selected fields)' }));

      expect(screen.getByRole('checkbox', { name: 'Source record storage (entire record)' })).toBeDisabled();
    });

    it('should close transformations modal when clicking on cancel button', () => {
      userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
      const modal = document.querySelector('.modalRoot');

      userEvent.click(getByRole(modal, 'button', { name: 'Cancel' }));

      return waitForElementToBeRemoved(() => document.querySelector('.modalRoot'));
    });

    it('should display validation error when record type is not selected', () => {
      userEvent.click(screen.getByRole('checkbox', { name: 'Item' }));
      userEvent.click(screen.getByRole('checkbox', { name: 'Item' }));

      expect(getByText(document.querySelector('[data-test-folio-record-type]'),
        'Please enter a value')).toBeVisible();
    });

    it('should display correct pane title', () => {
      expect(getByText(document.querySelector('[data-test-pane-header]'), 'New field mapping profile')).toBeVisible();
    });

    it('should not display empty transformation fields', () => {
      expect(screen.getByText('No transformations found')).toBeVisible();
    });

    it('should disable save button if there are no changes', () => {
      expect(screen.getByRole('button', { name: 'Save & close' })).toBeDisabled();
    });

    it('should not mark fields as error by default', () => {
      expect(document.querySelector('[data-test-folio-record-type-error]').childElementCount).toBe(0);
      expect(screen.getByLabelText('Name*').parentElement.classList.contains('hasError')).toBeFalsy();
    });

    describe('changing transformation field value', () => {
      let transformationFields;
      let modal;
      let transformationListRows;
      let footer;
      let formSubmitButton;

      beforeEach(() => {
        userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
        userEvent.click(screen.getByLabelText('Select all fields'));

        transformationFields = getTransformationFieldGroups();
        modal = document.querySelector('.modalRoot');

        userEvent.type(transformationFields[0].marcField.input, '123');
        userEvent.type(transformationFields[0].indicator1.input, '1');
        userEvent.type(transformationFields[0].indicator2.input, '0');
        userEvent.type(transformationFields[0].subfield.input, '$r');

        userEvent.type(transformationFields[1].marcField.input, '900');
        userEvent.type(transformationFields[1].subfield.input, '$1');

        userEvent.click(getByRole(modal, 'button', { name: 'Save & close' }));

        transformationListRows = getAllByRole(screen.getByRole('rowgroup'), 'row');
        footer = document.querySelector('[data-test-pane-footer-end]');
        formSubmitButton = getByRole(footer, 'button', { name: 'Save & close' });
      });

      it('should select all and add transformations with values', () => {
        expect(transformationListRows.length).toBe(2);
        expect(getByText(transformationListRows[0], '12310$r')).toBeVisible();
        expect(getByText(transformationListRows[1], '900 $1')).toBeVisible();
      });

      it('should enable submit button', () => {
        expect(formSubmitButton).not.toBeDisabled();
      });

      it('should display validation error when transformations do not match record type', () => {
        const folioRecordTypeContainer = document.querySelector('[data-test-folio-record-type]');

        userEvent.type(screen.getByLabelText('Name*'), 'Name');
        userEvent.click(getByRole(folioRecordTypeContainer, 'checkbox', { name: 'Item' }));

        userEvent.click(getByRole(footer, 'button', { name: 'Save & close' }));

        expect(getByText(document.querySelector('[data-test-folio-record-type]'),
          'Selected record types do not match specified transformations')).toBeVisible();
      });

      it('should disable submit button after clearing transformations', () => {
        userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
        userEvent.click(screen.getByLabelText('Select all fields'));
        userEvent.click(getByRole(modal, 'button', { name: 'Save & close' }));

        expect(formSubmitButton).toBeDisabled();
      });

      it('should not display validation for record types when SRS and holdings types are checked', () => {
        const folioRecordTypeContainer = document.querySelector('[data-test-folio-record-type]');

        userEvent.type(screen.getByLabelText('Name*'), 'Name');
        userEvent.click(getByRole(folioRecordTypeContainer, 'checkbox', { name: 'Holdings' }));
        userEvent.click(screen.getByRole('checkbox', { name: 'Source record storage (entire record)' }));
        userEvent.click(getByRole(footer, 'button', { name: 'Save & close' }));

        expect(queryByText(document.querySelector('[data-test-folio-record-type]'),
          'Selected record types do not match specified transformations')).toBeNull();
        expect(onSubmitMock).toBeCalled();
      });

      describe('reopening transformation modal', () => {
        beforeEach(async () => {
          userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));
        });

        it('should display proper amount of found transformations', () => {
          const modalHeader = document.querySelector('[data-test-pane-header-sub]');

          expect(getByText(modalHeader, '2 fields found')).toBeVisible();
        });

        it('should display proper total selected count', () => {
          const totalSelected = document.querySelector('[data-test-transformations-total-selected]');

          expect(getByText(totalSelected, 'Total selected: 2')).toBeVisible();
        });

        it('should have correct placeholders', () => {
          expect(transformationFields.length).toEqual(2);

          expect(queryByPlaceholderText(transformationFields[1].marcField.container, '900')).toBeNull();
          expect(queryByPlaceholderText(transformationFields[1].indicator1.container, '0')).toBeNull();
          expect(queryByPlaceholderText(transformationFields[1].indicator2.container, '0')).toBeNull();
          expect(queryByPlaceholderText(transformationFields[1].subfield.container, '$a')).toBeNull();
        });

        it('should have correct placeholders after the first transformation is selected and hidden after the selected items filter is unchecked', () => {
          userEvent.click(screen.getAllByRole('checkbox', { name: 'Select field' })[0]);
          userEvent.click(screen.getByRole('checkbox', { name: 'Selected' }));

          transformationFields = getTransformationFieldGroups();

          expect(transformationFields.length).toEqual(1);

          expect(queryByPlaceholderText(transformationFields[0].marcField.container, '900')).toBeNull();
          expect(queryByPlaceholderText(transformationFields[0].indicator1.container, '0')).toBeNull();
          expect(queryByPlaceholderText(transformationFields[0].indicator2.container, '0')).toBeNull();
          expect(queryByPlaceholderText(transformationFields[0].subfield.container, '$a')).toBeNull();
        });

        it('should display correct transformation fields values', () => {
          expect(transformationFields[0].marcField.input.value).toBe('123');
          expect(transformationFields[0].indicator1.input.value).toBe('1');
          expect(transformationFields[0].indicator2.input.value).toBe('0');
          expect(transformationFields[0].subfield.input.value).toBe('$r');

          expect(transformationFields[1].marcField.input.value).toBe('900');
          expect(transformationFields[1].indicator1.input.value).toBe('');
          expect(transformationFields[1].indicator2.input.value).toBe('');
          expect(transformationFields[1].indicator2.input.value).toBe('');
          expect(transformationFields[1].subfield.input.value).toBe('$1');
        });

        describe('unchecking transformation, clicking cancel and reopening modal', () => {
          it('should display correct selected field count', () => {
            const selectTransformationCheckboxes = screen.getAllByLabelText('Select field');

            userEvent.click(selectTransformationCheckboxes[0]);
            userEvent.click(getByRole(modal, 'button', { name: 'Cancel' }));
            userEvent.click(screen.getByRole('button', { name: 'Add transformations' }));

            const totalSelected = document.querySelector('[data-test-transformations-total-selected]');

            expect(getByText(totalSelected, 'Total selected: 2')).toBeVisible();
          });
        });
      });
    });
  });
});
Example #16
Source File: DuplicateMappingProfileRoute.test.js    From ui-data-export with Apache License 2.0 4 votes vote down vote up
describe('DuplicateMappingProfileRoute', () => {
  describe('rendering duplicate mapping profile page with profile data: success scenario', () => {
    const handleSubmitMock = jest.fn();
    const handleCancelMock = jest.fn();
    const handleSubmitNavigateMock = jest.fn();
    const sendCalloutMock = jest.fn();

    const renderDuplicateMappingProfileRoute = () => renderWithIntl(
      <DuplicateMappingProfileRouteContainer allTransformations={allMappingProfilesTransformations} />,
      translationsProperties
    );

    const renderDuplicateMappingProfileRouteWithMocks = () => renderWithIntl(
      <DuplicateMappingProfileRouteContainer
        allTransformations={allMappingProfilesTransformations}
        sendCallout={sendCalloutMock}
        onSubmit={handleSubmitMock}
        onSubmitNavigate={handleSubmitNavigateMock}
        onCancel={handleCancelMock}
      />,
      translationsProperties
    );

    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should display the form', () => {
      const { container } = renderDuplicateMappingProfileRoute();
      const form = container.querySelector('[data-test-full-screen-form]');

      expect(form).toBeVisible();
    });

    it('should display correct pane title', () => {
      renderDuplicateMappingProfileRoute();

      expect(getByText(paneHeader('New field mapping profile'), translations['mappingProfiles.newProfile'])).toBeVisible();
    });

    it('should have correct name field value', () => {
      renderDuplicateMappingProfileRoute();
      expect(nameField()).toHaveValue(`Copy of ${mappingProfile.name}`);
    });

    it('should display add transformations button with proper wording', () => {
      renderDuplicateMappingProfileRoute();

      expect(getByText(transformationsBtn('Add transformations'), translations['mappingProfiles.transformations.addTransformations'])).toBeVisible();
    });

    it('should have correct description field value', () => {
      renderDuplicateMappingProfileRoute();
      expect(descriptionField()).toHaveValue(mappingProfile.description);
    });

    it('should have enabled save button if there are no changes', () => {
      renderDuplicateMappingProfileRoute();
      expect(saveAndCloseBtn()).toBeEnabled();
    });

    it('should have correct folio record types field checked', () => {
      renderDuplicateMappingProfileRoute();

      expect(recordTypeInstance()).toBeChecked();
      expect(recordTypesSRS()).not.toBeChecked();
      expect(recordTypesHoldings()).not.toBeChecked();
      expect(recordTypesItem()).not.toBeChecked();
    });

    it('should display in form proper values', async () => {
      renderDuplicateMappingProfileRoute();

      userEvent.clear(nameField());
      userEvent.type(nameField(), 'Change name');
      userEvent.clear(descriptionField());
      userEvent.type(descriptionField(), 'Updated value');
      userEvent.type(recordTypesHoldings(), 'instance');
      userEvent.click(saveAndCloseBtn());

      expect(nameField()).toHaveValue('Change name');
      expect(descriptionField()).toHaveValue('Updated value');
      expect(recordTypesHoldings()).toBeChecked();
    });

    describe('clicking on cancel button', () => {
      it('should call cancel callback', () => {
        renderDuplicateMappingProfileRouteWithMocks();

        userEvent.click(screen.getByText(/cancel/i));

        expect(handleCancelMock).toHaveBeenCalled();
      });
    });

    describe('opening transformations modal', () => {
      it('should display correct transformations table with filled values', () => {
        renderDuplicateMappingProfileRoute();
        userEvent.click(transformationsBtn('Add transformations'));

        expect(columnHeaderFieldName()[0]).toBeVisible();
        expect(columnHeaderTransformation()[0]).toBeVisible();
        expect(transformationListCells()).toBeVisible();
      });

      it('should fill transformation field group on transformation modal correctly', () => {
        renderDuplicateMappingProfileRoute();

        userEvent.click(transformationsBtn('Add transformations'));

        const transformationFields = getTransformationFieldGroups();

        expect(transformationFields[2].marcField.input.value).toBe('900');
        expect(transformationFields[2].indicator1.input.value).toBe('');
        expect(transformationFields[2].indicator2.input.value).toBe('1');
        expect(transformationFields[2].subfield.input.value).toBe('$12');
      });

      it('should not show validation error when clearing transformation with empty indicator field', () => {
        const { container } = renderDuplicateMappingProfileRoute();

        userEvent.click(transformationsBtn('Add transformations'));

        const transformationFields = getTransformationFieldGroups();
        const modal = document.querySelector('[data-test-transformations-modal]');

        userEvent.type(transformationFields[2].marcField.input, '');
        userEvent.type(transformationFields[0].indicator2.input, '');
        userEvent.type(transformationFields[0].subfield.input, '');

        userEvent.dblClick(screen.getByLabelText('Select all fields'));

        userEvent.click(getByRole(modal, 'button', { name: 'Save & close' }));

        return waitForElementToBeRemoved(() => container.querySelector('[data-test-transformations-modal]'));
      });
    });

    it('should display transformation value', () => {
      renderDuplicateMappingProfileRoute();

      userEvent.click(transformationsBtn('Add transformations'));

      expect(getByText(transformationListRows()[0], '900 1$12')).toBeVisible();
    });

    describe('submitting the form - success case', () => {
      beforeEach(() => {
        handleSubmitMock.mockImplementationOnce(() => Promise.resolve());
      });

      it('should call submit callback', () => {
        renderDuplicateMappingProfileRouteWithMocks();

        userEvent.click(saveAndCloseBtn());

        expect(handleSubmitMock).toHaveBeenCalled();
      });

      it('should initiate displaying of success callout', async () => {
        renderDuplicateMappingProfileRouteWithMocks();

        userEvent.click(saveAndCloseBtn());

        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(handleSubmitNavigateMock).toHaveBeenCalled();
        });
      });
      it('should call submit callback with proper values', () => {
        renderDuplicateMappingProfileRouteWithMocks();

        userEvent.clear(nameField());
        userEvent.type(nameField(), 'Change name');
        userEvent.clear(descriptionField());
        userEvent.type(descriptionField(), 'Updated value');
        userEvent.click(saveAndCloseBtn());

        expect(nameField()).toHaveValue('Change name');
        expect(descriptionField()).toHaveValue('Updated value');
        expect(handleSubmitMock).toHaveBeenCalledWith(expect.objectContaining({
          name: 'Change name',
          description: 'Updated value',
        }));
      });
    });

    describe('submitting the form - error case', () => {
      beforeEach(() => {
        handleSubmitMock.mockImplementationOnce(() => Promise.reject());
      });

      it('should call submit callback', () => {
        renderDuplicateMappingProfileRouteWithMocks();

        userEvent.click(saveAndCloseBtn());

        expect(handleSubmitMock).toHaveBeenCalled();
      });

      it('should initiate displaying of error callout', async () => {
        renderDuplicateMappingProfileRouteWithMocks();

        userEvent.click(saveAndCloseBtn());

        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 #17
Source File: ChooseJobProfilePage.test.js    From ui-data-export with Apache License 2.0 4 votes vote down vote up
describe('ChooseJobProfile', () => {
  describe('rendering ChooseJobProfile', () => {
    const exportProfileSpy = jest.fn(Promise.resolve.bind(Promise));
    const pushHistorySpy = jest.fn();
    const mutator = buildMutator({ export: { POST: exportProfileSpy } });
    const location = { state: { fileDefinitionId: 'fileDefinitionId' } };
    let renderResult;

    beforeEach(() => {
      renderResult = renderWithIntl(
        <Router>
          <ChooseJobProfile
            resources={resources}
            mutator={mutator}
            history={{ push: pushHistorySpy }}
            location={location}
          />
        </Router>,
        translationsProperties
      );
    });

    afterEach(() => {
      jest.clearAllMocks();
    });

    it('should be visible', () => {
      const { container } = renderResult;

      expect(container.querySelector('#pane-results')).toBeVisible();
    });

    it('should display correct title and subtitle', () => {
      const { container } = renderResult;
      const paneHeader = container.querySelector('[data-test-pane-header]');

      expect(getByText(paneHeader, 'Select job profile to run the export')).toBeVisible();
      expect(getByText(paneHeader, '2 job profiles')).toBeVisible();
    });

    it('should place headers in correct order', () => {
      const { container } = renderResult;
      const headers = container.querySelectorAll('#search-results-list .mclHeader');

      expect(getByText(headers[0], 'Name')).toBeVisible();
      expect(getByText(headers[1], 'Description')).toBeVisible();
      expect(getByText(headers[2], 'Updated')).toBeVisible();
      expect(getByText(headers[3], 'Updated by')).toBeVisible();
    });

    it('should not display the confirmation modal', () => {
      const { container } = renderResult;
      const modal = container.querySelector('#choose-job-profile-confirmation-modal');

      expect(modal).not.toBeInTheDocument();
    });

    it('should display correct data for the first row', () => {
      const { container } = renderResult;
      const row = container.querySelector('.mclRow');
      const cells = row.querySelectorAll('.mclCell');

      expect(getByText(cells[0], 'A Lorem ipsum 1')).toBeVisible();
      expect(getByText(cells[1], 'Description 1')).toBeVisible();
      expect(getByText(cells[2], '12/4/2018')).toBeVisible();
      expect(getByText(cells[3], 'Donald S')).toBeVisible();
    });

    describe('clicking on row', () => {
      beforeEach(() => {
        const { container } = renderResult;
        const row = container.querySelector('.mclRow');

        userEvent.click(row);
      });

      it('should display modal with proper header', () => {
        expect(screen.getByText('Are you sure you want to run this job?')).toBeInTheDocument();
      });

      it('should display modal profile name in the body', () => {
        expect(screen.getByTestId('choose-job-select')).toBeVisible();
      });

      it('should display modal with proper wording for buttons', () => {
        const modal = document.querySelector('#choose-job-profile-confirmation-modal');

        expect(getByRole(modal, 'button', { name: 'Run' })).toBeVisible();
        expect(getByRole(modal, 'button', { name: 'Cancel' })).toBeVisible();
      });

      it('clicking on cancel button should close the modal', () => {
        const modal = document.querySelector('#choose-job-profile-confirmation-modal');

        userEvent.click(getByRole(modal, 'button', { name: 'Cancel' }));

        return waitForElementToBeRemoved(() => document.querySelector('#choose-job-profile-confirmation-modal'));
      });

      describe('clicking on confirm button - success case', () => {
        beforeEach(async () => {
          const modal = document.querySelector('#choose-job-profile-confirmation-modal');

          await userEvent.selectOptions(getByTestId(modal, 'choose-job-select'), 'Instances');

          userEvent.click(getByRole(modal, 'button', { name: 'Run' }));
        });

        it('should navigate to the landing page', () => {
          expect(pushHistorySpy).toHaveBeenCalledWith('/data-export');
        });
      });

      describe('clicking on confirm button - error case', () => {
        beforeEach(async () => {
          const modal = document.querySelector('#choose-job-profile-confirmation-modal');

          exportProfileSpy.mockImplementationOnce(Promise.reject.bind(Promise));

          await userEvent.selectOptions(getByTestId(modal, 'choose-job-select'), 'Instances');

          userEvent.click(getByRole(modal, 'button', { name: 'Run' }));
          await waitForElementToBeRemoved(() => document.querySelector('#choose-job-profile-confirmation-modal'));
        });

        it('should not navigate to the landing page', () => {
          expect(pushHistorySpy).not.toHaveBeenCalled();
        });
      });
    });
  });
});
Example #18
Source File: index.test.jsx    From apps-update-records with MIT License 4 votes vote down vote up
describe('UpdateRecordsApp', () => {
    const mutations = [];
    let addMutation = mutation => mutations.push(mutation);
    let testDriver;

    beforeEach(() => {
        testDriver = new TestDriver(recordListFixture);
        mutations.length = 0;
        testDriver.watch('mutation', addMutation);

        render(
            <testDriver.Container>
                <UpdateRecordsApp />
            </testDriver.Container>,
        );
    });

    afterEach(() => {
        testDriver.unwatch('mutations', addMutation);
    });

    async function clickUpdate(button) {
        await act(async () => {
            userEvent.click(button);
            await waitFor(() => expect(button.disabled).toBe(false), {timeout: 3000});
        });
    }

    it('renders a message with no button when another table is active', async () => {
        testDriver.setActiveCursorModels({table: 'Griddle cakes', view: 'Grid view 2'});

        await waitFor(() => expect(document.body.textContent).not.toBe(''));

        const {textContent} = document.body;
        expect(textContent).toMatch(/\bswitch\b/i);
        expect(textContent).toMatch(/\bInventory\b/);
        expect(screen.queryByRole('button')).toBe(null);
    });

    describe('disabled button', () => {
        it('correct active table with zero selected records', async () => {
            const button = await waitFor(() => screen.getByRole('button'));
            expect(button.disabled).toBe(true);
            expect(button.textContent).toMatch(/\b0\s+records\b/i);
        });

        it('correct active table with some selected records and underprivileged user', async () => {
            testDriver.simulatePermissionCheck(mutation => {
                return mutation.type !== 'setMultipleRecordsCellValues';
            });
            testDriver.userSelectRecords(['recb', 'recc']);

            const button = await waitFor(() => screen.getByRole('button'));
            expect(button.disabled).toBe(true);
            expect(button.textContent).toMatch(/\b2\s+records\b/i);
        });
    });

    it('renders a single enabled button for correct active table with some selected records', async () => {
        testDriver.userSelectRecords(['reca', 'recc']);
        const button = await waitFor(() => screen.getByRole('button'));
        expect(button.disabled).toBe(false);
        expect(button.textContent).toMatch(/\b2\s+records\b/i);
    });

    it('updates selected records only', async () => {
        testDriver.userSelectRecords(['recb']);
        const button = await waitFor(() => screen.getByRole('button'));

        await clickUpdate(button);

        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'setMultipleRecordsCellValues',
                    tableId: 'tblTable',
                    records: [
                        {
                            id: 'recb',
                            cellValuesByFieldId: {fldInStock: 15},
                        },
                    ],
                },
            ]),
        );
    });

    it('batches updates', async () => {
        const biggerFixture = JSON.parse(JSON.stringify(recordListFixture));
        const extraRecords = Array.from(Array(321)).map((_, index) => ({
            id: `rec${index}`,
            commentCount: 0,
            createdTime: '2020-11-04T23:20:14.000Z',
            cellValuesByFieldId: {
                fldName: `crepe #${index}`,
                fldInStock: 0,
            },
        }));
        biggerFixture.base.tables[0].records.push(...extraRecords);
        testDriver = new TestDriver(biggerFixture);
        testDriver.watch('mutation', addMutation);
        testDriver.userSelectRecords(extraRecords.map(({id}) => id));

        document.body.innerHTML = '';
        render(
            <testDriver.Container>
                <UpdateRecordsApp />
            </testDriver.Container>,
        );

        const button = await waitFor(() => screen.getByRole('button'));
        await waitFor(() => expect(button.disabled).toBe(false));

        await clickUpdate(button);

        const batchSizes = mutations
            .filter(({type}) => type === 'setMultipleRecordsCellValues')
            .map(({records}) => records.length);

        for (const batchSize of batchSizes) {
            expect(batchSize).toBeLessThan(51);
        }

        expect(batchSizes.reduce((a, b) => a + b)).toBe(321);
    });

    it('maintains selection through multiple updates', async () => {
        testDriver.userSelectRecords(['recc']);
        const button = await waitFor(() => screen.getByRole('button'));

        await clickUpdate(button);

        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'setMultipleRecordsCellValues',
                    tableId: 'tblTable',
                    records: [
                        {
                            id: 'recc',
                            cellValuesByFieldId: {fldInStock: 9},
                        },
                    ],
                },
            ]),
        );

        mutations.length = 0;
        await clickUpdate(button);

        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'setMultipleRecordsCellValues',
                    tableId: 'tblTable',
                    records: [
                        {
                            id: 'recc',
                            cellValuesByFieldId: {fldInStock: 10},
                        },
                    ],
                },
            ]),
        );
    });
});
Example #19
Source File: index.jsdom.test.jsx    From apps-todo-list with MIT License 4 votes vote down vote up
describe('TodoApp', () => {
    let mutations;
    let addMutation = mutation => mutations.push(mutation);
    let testDriver;

    beforeEach(() => {
        testDriver = new TestDriver(recordListFixture);
        mutations = [];
        testDriver.watch('mutation', addMutation);

        render(
            <testDriver.Container>
                <TodoApp />
            </testDriver.Container>,
        );
    });

    afterEach(() => {
        testDriver.unwatch('mutations', addMutation);
    });

    it('renders a list of records (user with "write" permissions)', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        const items = readItems();

        expect(items.length).toBe(3);

        expect(items).toEqual([
            {checked: false, text: 'carrots'},
            {checked: true, text: 'baby carrots'},
            {checked: false, text: 'elderly carrots'},
        ]);
    });

    // This test cannot be fully expressed using the capabilities currently
    // available in the SDK.
    it('renders a list of records (user without "write" permissions)', async () => {
        testDriver.simulatePermissionCheck(mutation => {
            return mutation.type === 'setMultipleGlobalConfigPaths';
        });

        await openAsync('Groceries', 'Grid view', 'Purchased');

        expect(screen.getByRole('button', {name: 'Add'}).disabled).toBe(true);

        const items = getItems().map(item => ({
            checked: item.checkbox.checked,
            text: item.container.textContent.trim(),
            checkboxDisabled: item.checkbox.disabled,
            deleteButtonDisabled: item.deleteButton.disabled,
        }));

        expect(items.length).toBe(3);

        expect(items).toEqual([
            {checked: false, text: 'carrots', checkboxDisabled: true, deleteButtonDisabled: true},
            {
                checked: true,
                text: 'baby carrots',
                checkboxDisabled: true,
                deleteButtonDisabled: true,
            },
            {
                checked: false,
                text: 'elderly carrots',
                checkboxDisabled: true,
                deleteButtonDisabled: true,
            },
        ]);
    });

    it('gracefully handles the deletion of fields', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        await act(() => testDriver.deleteFieldAsync('tblTable1', 'fldPurchased'));

        const items = readItems();

        expect(items).toEqual([]);
    });

    it('gracefully handles the deletion of tables', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        act(() => {
            testDriver.deleteTable('tblTable1');
        });

        const items = readItems();

        expect(items).toEqual([]);

        const options = getAllByRole(screen.getByLabelText('Table'), 'option');

        expect(options.map(getNodeText)).toEqual(['Pick a table...', 'Porcelain dolls']);

        expect(options[0].selected).toBe(true);
        expect(screen.queryByLabelText('View')).toBe(null);
        expect(screen.queryByLabelText('Field')).toBe(null);
    });

    it('gracefully handles the deletion of views', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        await act(() => testDriver.deleteViewAsync('tblTable1', 'viwGridView'));

        const items = readItems();

        expect(items).toEqual([]);

        const tableOptions = getAllByRole(screen.getByLabelText('Table'), 'option');

        expect(tableOptions.map(getNodeText)).toEqual([
            'Pick a table...',
            'Groceries',
            'Porcelain dolls',
        ]);

        expect(tableOptions[1].selected).toBe(true);

        const viewOptions = getAllByRole(screen.getByLabelText('View'), 'option');
        expect(viewOptions.map(getNodeText)).toEqual(['Pick a view...', 'Another grid view']);
        expect(viewOptions[0].selected).toBe(true);

        const fieldOptions = getAllByRole(screen.getByLabelText('Field'), 'option');
        expect(fieldOptions.map(getNodeText)).toEqual([
            "Pick a 'done' field...",
            'Name',
            'Purchased',
        ]);
        expect(fieldOptions[2].selected).toBe(true);
    });

    it('allows records to be created without a name', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        const initialCount = readItems().length;

        userEvent.click(screen.getByRole('button', {name: 'Add'}));

        const items = readItems();

        expect(items.length).toBe(initialCount + 1);
        expect(items.pop()).toEqual({
            checked: false,
            text: 'Unnamed record',
        });

        await waitFor(() => expect(mutations.length).not.toBe(0));
        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'createMultipleRecords',
                    tableId: 'tblTable1',
                    records: [
                        {
                            id: expect.anything(),
                            cellValuesByFieldId: {
                                fldName: '',
                            },
                        },
                    ],
                },
            ]),
        );
    });

    it('allows multiple records to be created with a name', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        const initialCount = readItems().length;

        userEvent.type(screen.getByRole('textbox'), 'brash teenaged carrots');
        userEvent.click(screen.getByRole('button', {name: 'Add'}));

        let items = readItems();

        expect(items.length).toBe(initialCount + 1);
        expect(items.pop()).toEqual({
            checked: false,
            text: 'brash teenaged carrots',
        });

        await waitFor(() => expect(mutations.length).not.toBe(0));
        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'createMultipleRecords',
                    tableId: 'tblTable1',
                    records: [
                        {
                            id: expect.anything(),
                            cellValuesByFieldId: {
                                fldName: 'brash teenaged carrots',
                            },
                        },
                    ],
                },
            ]),
        );

        mutations.length = 0;

        userEvent.type(screen.getByRole('textbox'), 'parsnips');
        userEvent.click(screen.getByRole('button', {name: 'Add'}));

        items = readItems();

        expect(items.length).toBe(initialCount + 2);

        expect(items.pop()).toEqual({
            checked: false,
            text: 'parsnips',
        });

        await waitFor(() => expect(mutations.length).not.toBe(0));
        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'createMultipleRecords',
                    tableId: 'tblTable1',
                    records: [
                        {
                            id: expect.anything(),
                            cellValuesByFieldId: {
                                fldName: 'parsnips',
                            },
                        },
                    ],
                },
            ]),
        );
    });

    it('allows records to be destroyed', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        userEvent.click(getItems()[1].deleteButton);

        const items = readItems();

        expect(items).toEqual([
            {checked: false, text: 'carrots'},
            {checked: false, text: 'elderly carrots'},
        ]);

        await waitFor(() => expect(mutations.length).not.toBe(0));
        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'deleteMultipleRecords',
                    tableId: 'tblTable1',
                    recordIds: ['recb'],
                },
            ]),
        );
    });

    it('allows records to be marked as "complete"', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        userEvent.click(getItems()[0].checkbox);

        const items = readItems();

        expect(items[0]).toEqual({checked: true, text: 'carrots'});

        await waitFor(() => expect(mutations.length).not.toBe(0));
        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'setMultipleRecordsCellValues',
                    tableId: 'tblTable1',
                    records: [
                        {
                            id: 'reca',
                            cellValuesByFieldId: {
                                fldPurchased: true,
                            },
                        },
                    ],
                },
            ]),
        );
    });

    it('allows records to be marked as "incomplete"', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');

        userEvent.click(getItems()[1].checkbox);

        const items = readItems();

        expect(items[1]).toEqual({checked: false, text: 'baby carrots'});

        await waitFor(() => expect(mutations.length).not.toBe(0));
        expect(mutations).toEqual(
            expect.arrayContaining([
                {
                    type: 'setMultipleRecordsCellValues',
                    tableId: 'tblTable1',
                    records: [
                        {
                            id: 'recb',
                            cellValuesByFieldId: {
                                fldPurchased: false,
                            },
                        },
                    ],
                },
            ]),
        );
    });

    it('expands records upon click', async () => {
        await openAsync('Groceries', 'Grid view', 'Purchased');
        const recordIds = [];
        testDriver.watch('expandRecord', ({recordId}) => recordIds.push(recordId));

        userEvent.click(getItems()[0].link);
        await waitFor(() => expect(recordIds.length).not.toBe(0));

        expect(recordIds).toEqual(['reca']);
    });
});