@testing-library/react#queryByText JavaScript Examples

The following examples show how to use @testing-library/react#queryByText. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Login.test.js    From viade_en1b with MIT License 6 votes vote down vote up
describe("login component", () => {
  test("renders the header correctly", async () => {
    const text = "Login";
    waitForElement(() => {
      expect(queryByTestId(rendered, "login-header")).toEqual("Login");
      expect(queryByTestId(rendered, "login-header")).not.toBeNull();
    });
  });

  test("pop up opens up", () => {
    waitForElement(() => {
      global.open = jest.fn();
      fireEvent.click(queryByText(rendered, "Login here!"));
      expect(global.open).toBeCalled();
    });
  });
});
Example #2
Source File: UploadRoute.test.js    From viade_en1b with MIT License 6 votes vote down vote up
describe("Renders correctly", () => {
  test("All labels are rendered", () => {
    let nameLabel = queryByText(uploadRoute, "Name of the route");
    let descriptionLabel = queryByText(uploadRoute, "Description");
    expect(nameLabel).toBeInTheDocument();
    expect(descriptionLabel).toBeInTheDocument();
  });

  test("all inputs are rendered", () => {
    let nameInput = queryByPlaceholderText(uploadRoute, "Route name");
    let descriptionInput = queryByPlaceholderText(
      uploadRoute,
      "Add a description"
    );
    expect(nameInput).toBeInTheDocument();
    expect(descriptionInput).toBeInTheDocument();
  });
  test("submit button is rendered", () => {
    let submitButton = queryByText(uploadRoute, "Submit");
    expect(submitButton).toBeInTheDocument();
  });
});
Example #3
Source File: FormPanel.spec.js    From beautiful-react-ui with MIT License 6 votes vote down vote up
describe('FormPanel component', () => {
  afterEach(cleanup);

  const defaultProps = {};

  // performs the standard tests
  performStandardTests(FormPanel, defaultProps);
  // performs a test on default class names
  hasDefaultClassNames(FormPanel, defaultProps, ['bi-form-panel', 'bi-fp-label-floating']);

  it('shall allow to define a label', () => {
    const { container } = render(<FormPanel label="Foo" />);
    const labelEl = queryByText(container, 'Foo');

    expect(labelEl).to.exist;
    expect(labelEl.classList.contains('bi-fp-label')).to.be.true;
  });
});
Example #4
Source File: SupportedFeature.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 6 votes vote down vote up
describe('SupportedFeature', () => {
  const name = messages['featureName-basic-configuration'].defaultMessage;
  let container;

  beforeEach(() => {
    const wrapper = render(
      <SupportedFeature
        name={name}
      />,
    );
    container = wrapper.container;
  });

  test('displays a check icon ', () => {
    expect(container.querySelector('svg')).toHaveAttribute('id', 'check-icon');
  });

  test('displays feature name', () => {
    expect(queryByText(container, name)).toBeInTheDocument();
  });
});
Example #5
Source File: UploadRoute.test.js    From viade_en1b with MIT License 5 votes vote down vote up
describe("Behaviour", () => {
  test("all inputs are cleared after submit", () => {
    let nameInput = queryByPlaceholderText(uploadRoute, "Route name");
    let submitButton = queryByText(uploadRoute, "Submit");
    let descriptionInput = queryByPlaceholderText(
      uploadRoute,
      "Add a description"
    );

    expect(nameInput).toBeEmpty();
    expect(descriptionInput).toBeEmpty();

    fireEvent.change(nameInput, { target: { value: "mock" } });
    fireEvent.change(descriptionInput, { target: { value: "mock" } });

    expect(nameInput.value).toBe("mock");
    expect(descriptionInput.value).toBe("mock");

    submitButton.click();
    waitForDomChange(() => {
      expect(nameInput).toBeEmpty();
      expect(descriptionInput).toBeEmpty();
    });
  });

  test("submit function is triggered when the form is submitted", () => {
    const spy = jest.spyOn(UploadRoute.prototype, "submitForm");
    const mockFunc = jest.fn();
    rerenderFunc(
      <IntlProvider key={"en"} locale={"en"} messages={locales["en"]}>
        <UploadRoute
          routes={[]}
          uploadRoute={mockFunc}
          loadRoutes={mockFunc}
        ></UploadRoute>
      </IntlProvider>
    );
    let submitButton = queryByText(uploadRoute, "Submit");
    expect(spy).not.toBeCalled();
    submitButton.click();
    waitForDomChange(() => {
      expect(mockFunc).toBeCalled();
    });
  });

  test("function handling the information of the route is fired", () => {
    const spy = jest.spyOn(UploadRoute.prototype, "changeHandlerRoute");
    const mockFunc = jest.fn();

    let nameInput = queryByPlaceholderText(uploadRoute, "Route name");
    let descriptionInput = queryByPlaceholderText(
      uploadRoute,
      "Add a description"
    );
    rerenderFunc(
      <IntlProvider key={"en"} locale={"en"} messages={locales["en"]}>
        <UploadRoute
          routes={[]}
          uploadRoute={mockFunc}
          loadRoutes={mockFunc}
        ></UploadRoute>
      </IntlProvider>
    );

    expect(spy).not.toBeCalled();

    fireEvent.change(nameInput, { target: { value: "mock" } });
    expect(spy).toBeCalled();
    fireEvent.change(descriptionInput, { target: { value: "mock" } });
    expect(spy).toBeCalled();
  });
});
Example #6
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 #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: 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 #9
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 #10
Source File: Avatar.spec.js    From beautiful-react-ui with MIT License 4 votes vote down vote up
describe('Avatar component', () => {
  afterEach(cleanup);

  const defaultProps = { src: 'foo' };

  performStandardTests(Avatar, defaultProps);

  it('should render and image if src prop is provided', () => {
    const { container } = render(<Avatar src="foo" />);
    const avatar = container.querySelector('.bi.bi-avatar');
    const image = avatar.querySelector('img');

    expect(image).to.exist;
    expect(image.getAttribute('src')).to.equal('foo');
  });

  it('should render the username initials if the `initials` prop is provided', () => {
    const { container } = render(<Avatar src="foo" initials="ar" />);
    const avatar = container.querySelector('.bi.bi-avatar');
    const initials = avatar.querySelector('.initials');

    expect(initials).to.exist;
  });

  it('should allow to define the avatar size', () => {
    const { container, rerender } = render(<Avatar src="foo" size="small" />);
    const avatar = container.querySelector('.bi.bi-avatar');

    expect(avatar.getAttribute('class').split(' ')).to.include.members(['avt-sm']);

    rerender(<Avatar src="foo" size="large" />);
    expect(avatar.getAttribute('class').split(' ')).to.include.members(['avt-lg']);

    rerender(<Avatar src="foo" />);
    expect(avatar.getAttribute('class').split(' ')).to.not.include.members(['avt-lg', 'avt-sm']);
  });

  it('should allow to define the avatar shape', () => {
    const { container, rerender } = render(<Avatar src="foo" shape="square" />);
    const avatar = container.querySelector('.bi.bi-avatar');

    expect(avatar.getAttribute('class').split(' ')).to.include.members(['avt-square']);

    rerender(<Avatar src="foo" />);
    expect(avatar.getAttribute('class').split(' ')).to.include.members(['avt-rounded']);
  });

  it('should possibly have an alternative text', () => {
    const text = 'lorem ipsum';
    const { container } = render(<Avatar src="foo" alt={text} />);
    const avatar = container.querySelector('.bi.bi-avatar');

    expect(avatar.querySelector('img').getAttribute('alt')).to.equal(text);
  });

  it('should possibly render a pill component if given as a prop', () => {
    const { container } = render(<Avatar src="foo" pill={<Pill>foo</Pill>} />);
    const pill = container.querySelector('.bi.bi-avatar').querySelector('.bi.bi-pill');

    expect(pill).to.exist;
    expect(pill.tagName).to.equal('SPAN');
  });

  it('should possibly render the online/offline state if provided', () => {
    const { container, rerender } = render(<Avatar src="foo" state="online" />);
    const avatar = container.querySelector('.bi.bi-avatar');

    expect(avatar.querySelector('.avt-state')).to.exist;
    expect(avatar.querySelector('.avt-state').classList.contains('state-online')).to.be.true;

    rerender(<Avatar src="foo" state="offline" />);

    expect(avatar.querySelector('.avt-state').classList.contains('state-offline')).to.be.true;
  });

  it('should possibly render further information if provided', () => {
    const displayName = 'Name';
    const furtherInfo = 'Role';
    const { container } = render(<Avatar src="foo" displayName={displayName} furtherInfo={furtherInfo} />);
    const nameElement = queryByText(container, displayName);
    const infoElement = queryByText(container, furtherInfo);

    expect(nameElement).to.exist;
    expect(nameElement.classList.contains('avtr-disp-name')).to.be.true;

    expect(infoElement).to.exist;
    expect(infoElement.classList.contains('avtr-furth-info')).to.be.true;
  });

  it('should return null iof no src nor initials are provided', () => {
    const { container } = render(<Avatar />);
    const avatar = container.querySelector('.bi.bi-avatar');

    expect(avatar).to.not.exist;
  });
});
Example #11
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 #12
Source File: OpenedXConfigForm.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('OpenedXConfigForm', () => {
  let axiosMock;
  let store;
  let container;

  beforeEach(async () => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: true,
        roles: [],
      },
    });
    axiosMock = new MockAdapter(getAuthenticatedHttpClient());
    store = initializeStore();
  });

  afterEach(() => {
    axiosMock.reset();
  });

  const createComponent = (onSubmit = jest.fn(), formRef = createRef(), legacy = true) => {
    const wrapper = render(
      <AppProvider store={store}>
        <IntlProvider locale="en">
          <OpenedXConfigForm
            onSubmit={onSubmit}
            formRef={formRef}
            legacy={legacy}
          />
        </IntlProvider>
      </AppProvider>,
    );
    container = wrapper.container;
    return container;
  };

  const mockStore = async (mockResponse) => {
    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);
    store.dispatch(selectApp({ appId: 'legacy' }));
  };

  test('title rendering', async () => {
    await mockStore(legacyApiResponse);
    createComponent();
    expect(container.querySelector('h3')).toHaveTextContent('edX');
  });

  test('new Open edX provider config', async () => {
    await mockStore({ ...legacyApiResponse, enable_in_context: true });
    createComponent(jest.fn(), createRef(), false);
    expect(queryByText(container, messages.visibilityInContext.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.gradedUnitPagesLabel.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.groupInContextSubsectionLabel.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.allowUnitLevelVisibilityLabel.defaultMessage)).toBeInTheDocument();
  });

  test('calls onSubmit when the formRef is submitted', async () => {
    const formRef = createRef();
    const handleSubmit = jest.fn();

    await mockStore(legacyApiResponse);
    createComponent(handleSubmit, formRef);

    await act(async () => {
      formRef.current.submit();
    });

    expect(handleSubmit).toHaveBeenCalledWith(
      // Because we use defaultAppConfig as the initialValues of the form, and we haven't changed
      // any of the form inputs, this exact object shape is returned back to us, so we're reusing
      // it here.  It's not supposed to be 'the same object', it just happens to be.
      {
        ...defaultAppConfig(),
        divideByCohorts: false,
        divisionScheme: DivisionSchemes.COHORT,
      },
    );
  });

  test('default field states are correct, including removal of folded sub-fields', async () => {
    await mockStore({
      ...legacyApiResponse,
      plugin_configuration: {
         ...legacyApiResponse.plugin_configuration,
        reported_content_email_notifications_flag: true,
        divided_course_wide_discussions: [],
      },
    });
    createComponent();
    const { divideDiscussionIds } = defaultAppConfig(['13f106c6-6735-4e84-b097-0456cff55960', 'course']);

    // DivisionByGroupFields
    expect(container.querySelector('#divideByCohorts')).toBeInTheDocument();
    expect(container.querySelector('#divideByCohorts')).not.toBeChecked();
    expect(container.querySelector('#divideCourseTopicsByCohorts')).not.toBeInTheDocument();

    divideDiscussionIds.forEach(id => expect(
      container.querySelector(`#checkbox-${id}`),
    ).not.toBeInTheDocument());

    // AnonymousPostingFields
    expect(
      container.querySelector('#allowAnonymousPostsPeers'),
    ).toBeInTheDocument();
    expect(container.querySelector('#allowAnonymousPostsPeers')).not.toBeChecked();

    // ReportedContentEmailNotifications
    expect(container.querySelector('#reportedContentEmailNotifications')).toBeInTheDocument();
    expect(container.querySelector('#reportedContentEmailNotifications')).not.toBeChecked();

    // BlackoutDatesField
    expect(queryByText(container, messages.blackoutDatesLabel.defaultMessage)).toBeInTheDocument();
  });

  test('folded sub-fields are in the DOM when parents are enabled', async () => {
    await mockStore({
      ...legacyApiResponse,
      plugin_configuration: {
        ...legacyApiResponse.plugin_configuration,
        allow_anonymous: true,
        reported_content_email_notifications: true,
        reported_content_email_notifications_flag: true,
        always_divide_inline_discussions: true,
        divided_course_wide_discussions: [],
      },
    });
    createComponent();
    const { divideDiscussionIds } = defaultAppConfig(['13f106c6-6735-4e84-b097-0456cff55960', 'course']);

    // DivisionByGroupFields
    expect(container.querySelector('#divideByCohorts')).toBeInTheDocument();
    expect(container.querySelector('#divideByCohorts')).toBeChecked();
    expect(
      container.querySelector('#divideCourseTopicsByCohorts'),
    ).toBeInTheDocument();
    expect(
      container.querySelector('#divideCourseTopicsByCohorts'),
    ).not.toBeChecked();

    divideDiscussionIds.forEach(id => expect(
      container.querySelector(`#checkbox-${id}`),
    ).not.toBeInTheDocument());

    // AnonymousPostingFields
    expect(
      container.querySelector('#allowAnonymousPostsPeers'),
    ).toBeInTheDocument();
    expect(
      container.querySelector('#allowAnonymousPostsPeers'),
    ).not.toBeChecked();

    // ReportedContentEmailNotifications
    expect(container.querySelector('#reportedContentEmailNotifications')).toBeInTheDocument();
    expect(container.querySelector('#reportedContentEmailNotifications')).toBeChecked();
  });

  test('folded discussion topics are in the DOM when divideByCohorts and divideCourseWideTopics are enabled',
    async () => {
      await mockStore({
        ...legacyApiResponse,
        plugin_configuration: {
          ...legacyApiResponse.plugin_configuration,
          allow_anonymous: true,
          reported_content_email_notifications: true,
          reported_content_email_notifications_flag: true,
          always_divide_inline_discussions: true,
          divided_course_wide_discussions: ['13f106c6-6735-4e84-b097-0456cff55960', 'course'],
        },
      });
      createComponent();
      const { divideDiscussionIds } = defaultAppConfig(['13f106c6-6735-4e84-b097-0456cff55960', 'course']);

      // DivisionByGroupFields
      expect(container.querySelector('#divideByCohorts')).toBeInTheDocument();
      expect(container.querySelector('#divideByCohorts')).toBeChecked();
      expect(container.querySelector('#divideCourseTopicsByCohorts')).toBeInTheDocument();
      expect(container.querySelector('#divideCourseTopicsByCohorts')).toBeChecked();

      divideDiscussionIds.forEach(id => {
        expect(container.querySelector(`#checkbox-${id}`)).toBeInTheDocument();
        expect(container.querySelector(`#checkbox-${id}`)).toBeChecked();
      });
    });

  const updateTopicName = async (topicId, topicName) => {
    const topicCard = queryByTestId(container, topicId);

    await act(async () => { userEvent.click(queryByLabelText(topicCard, 'Expand')); });
    const topicInput = topicCard.querySelector('input');
    topicInput.focus();
    await act(async () => { fireEvent.change(topicInput, { target: { value: topicName } }); });
    topicInput.blur();

    return topicCard;
  };

  const assertTopicNameRequiredValidation = (topicCard, expectExists = true) => {
    const error = queryByText(topicCard, messages.discussionTopicRequired.defaultMessage);
    if (expectExists) { expect(error).toBeInTheDocument(); } else { expect(error).not.toBeInTheDocument(); }
  };

  const assertDuplicateTopicNameValidation = async (topicCard, expectExists = true) => {
    const error = queryByText(topicCard, messages.discussionTopicNameAlreadyExist.defaultMessage);
    if (expectExists) { expect(error).toBeInTheDocument(); } else { expect(error).not.toBeInTheDocument(); }
  };

  const assertHasErrorValidation = (expectExists = true) => {
    expect(store.getState().discussions.hasValidationError).toBe(expectExists);
  };

  test('show required error on field when leaving empty topic name',
    async () => {
      await mockStore(legacyApiResponse);
      createComponent();

      const topicCard = await updateTopicName('13f106c6-6735-4e84-b097-0456cff55960', '');
      await waitForElementToBeRemoved(queryByText(topicCard, messages.addTopicHelpText.defaultMessage));
      assertTopicNameRequiredValidation(topicCard);
      assertHasErrorValidation();
    });

  test('check field is not collapsible in case of error', async () => {
    await mockStore(legacyApiResponse);
    createComponent();

    const topicCard = await updateTopicName('13f106c6-6735-4e84-b097-0456cff55960', '');
    const collapseButton = queryByLabelText(topicCard, 'Collapse');
    await act(async () => userEvent.click(collapseButton));

    expect(collapseButton).toBeInTheDocument();
  });

  describe('Duplicate validation test cases', () => {
    let topicCard;
    let duplicateTopicCard;

    beforeEach(async () => {
      await mockStore(legacyApiResponse);
      createComponent();

      topicCard = await updateTopicName('course', 'edx');
      duplicateTopicCard = await updateTopicName('13f106c6-6735-4e84-b097-0456cff55960', 'EDX');
    });

    test('show duplicate errors on fields when passing duplicate topic name', async () => {
      await assertDuplicateTopicNameValidation(topicCard);
      await assertDuplicateTopicNameValidation(duplicateTopicCard);
      assertHasErrorValidation();
    });

    test('check duplicate error is removed on fields when name is fixed', async () => {
      const duplicateTopicInput = duplicateTopicCard.querySelector('input');
      duplicateTopicInput.focus();
      await act(async () => { userEvent.type(duplicateTopicInput, 'valid'); });
      duplicateTopicInput.blur();

      await waitForElementToBeRemoved(
        queryAllByText(topicCard, messages.discussionTopicNameAlreadyExist.defaultMessage),
      );

      await assertDuplicateTopicNameValidation(duplicateTopicCard, false);
      await assertDuplicateTopicNameValidation(topicCard, false);
      assertHasErrorValidation(false);
    });

    test('check duplicate error is removed on deleting duplicate topic', async () => {
      await act(async () => {
        userEvent.click(
          queryByLabelText(duplicateTopicCard, messages.deleteAltText.defaultMessage, { selector: 'button' }),
        );
      });

      await act(async () => {
        userEvent.click(
          queryByRole(container, 'button', { name: messages.deleteButton.defaultMessage }),
        );
      });

      await waitForElementToBeRemoved(queryByText(topicCard, messages.discussionTopicNameAlreadyExist.defaultMessage));

      expect(duplicateTopicCard).not.toBeInTheDocument();
      await assertDuplicateTopicNameValidation(topicCard, false);
      assertHasErrorValidation(false);
    });
  });
});
Example #13
Source File: DiscussionTopics.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('DiscussionTopics', () => {
  let axiosMock;
  let store;
  let container;
  beforeEach(() => {
    initializeMockApp({
      authenticatedUser: {
        userId: 3,
        username: 'abc123',
        administrator: true,
        roles: [],
      },
    });

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

  afterEach(() => {
    axiosMock.reset();
  });

  const createComponent = (data) => {
    const wrapper = render(
      <AppProvider store={store}>
        <IntlProvider locale="en">
          <OpenedXConfigFormProvider value={contextValue}>
            <Formik initialValues={data}>
              <DiscussionTopics />
            </Formik>
          </OpenedXConfigFormProvider>
        </IntlProvider>
      </AppProvider>,
    );
    container = wrapper.container;
  };

  const mockStore = async (mockResponse) => {
    axiosMock.onGet(getDiscussionsProvidersUrl(courseId)).reply(200, mockResponse);
    await executeThunk(fetchProviders(courseId), store.dispatch);
  };

  test('renders each discussion topic correctly', async () => {
    await mockStore(legacyApiResponse);
    createComponent(appConfig);
    await waitFor(() => {
      expect(queryAllByTestId(container, 'course')).toHaveLength(1);
      expect(queryAllByTestId(container, '13f106c6-6735-4e84-b097-0456cff55960')).toHaveLength(1);
    });
  });

  test('renders discussion topic heading, label and helping text', async () => {
    await mockStore(legacyApiResponse);
    createComponent(appConfig);
    await waitFor(() => {
      expect(queryByText(container, messages.discussionTopics.defaultMessage)).toBeInTheDocument();
      expect(queryByText(container, messages.discussionTopicsLabel.defaultMessage)).toBeInTheDocument();
      expect(queryByText(container, messages.discussionTopicsHelp.defaultMessage)).toBeInTheDocument();
    });
  });

  test('add topic button is rendered correctly', async () => {
    await mockStore(legacyApiResponse);
    createComponent(appConfig);
    await waitFor(() => {
      expect(queryByText(container, messages.addTopicButton.defaultMessage, { selector: 'button' }))
        .toBeInTheDocument();
    });
  });

  test('calls "onClick" callback when add topic button is clicked', async () => {
    await mockStore(legacyApiResponse);
    createComponent(appConfig);

    const addTopicButton = queryByText(container, messages.addTopicButton.defaultMessage, { selector: 'button' });
    await waitFor(async () => {
      expect(queryByText(container, messages.configureAdditionalTopic.defaultMessage)).not.toBeInTheDocument();
      await act(async () => fireEvent.click(addTopicButton));
      expect(queryByText(container, messages.configureAdditionalTopic.defaultMessage)).toBeInTheDocument();
    });
  });

  test('updates discussion topic name', async () => {
    await mockStore(legacyApiResponse);
    createComponent(appConfig);
    const topicCard = queryByTestId(container, '13f106c6-6735-4e84-b097-0456cff55960');

    await act(async () => userEvent.click(queryByLabelText(topicCard, 'Expand')));
    await act(async () => {
      fireEvent.change(topicCard.querySelector('input'), { target: { value: 'new name' } });
    });
    await act(async () => userEvent.click(queryByLabelText(topicCard, 'Collapse')));

    expect(queryByText(topicCard, 'new name')).toBeInTheDocument();
  });
});
Example #14
Source File: TopicItem.test.jsx    From frontend-app-course-authoring with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('TopicItem', () => {
  let axiosMock;
  let store;
  let container;

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

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

  afterEach(() => {
    axiosMock.reset();
  });

  const createComponent = (props) => {
    const wrapper = render(
      <AppProvider store={store}>
        <IntlProvider locale="en">
          <Formik initialValues={appConfig}>
            <TopicItem {...props} />
          </Formik>
        </IntlProvider>
      </AppProvider>,
    );
    container = wrapper.container;
  };

  const mockStore = async (mockResponse) => {
    axiosMock.onGet(getDiscussionsProvidersUrl(courseId)).reply(200, mockResponse);
    await executeThunk(fetchProviders(courseId), store.dispatch);
  };

  test('displays a collapsible card for discussion topic', async () => {
    await mockStore(legacyApiResponse);
    createComponent(generalTopic);

    expect(queryAllByTestId(container, 'course')).toHaveLength(1);
  });

  test('displays collapse view of general discussion topic', async () => {
    await mockStore(legacyApiResponse);
    createComponent(generalTopic);

    const generalTopicNode = queryByTestId(container, 'course');
    expect(queryByLabelText(generalTopicNode, 'Expand')).toBeInTheDocument();
    expect(queryByLabelText(generalTopicNode, 'Collapse')).not.toBeInTheDocument();
    expect(queryByText(generalTopicNode, 'General')).toBeInTheDocument();
  });

  test('displays expand view of general discussion topic', async () => {
    await mockStore(legacyApiResponse);
    createComponent(generalTopic);

    const generalTopicNode = queryByTestId(container, 'course');
    userEvent.click(queryByLabelText(generalTopicNode, 'Expand'));

    expect(queryByLabelText(generalTopicNode, 'Expand')).not.toBeInTheDocument();
    expect(queryByLabelText(generalTopicNode, 'Collapse')).toBeInTheDocument();
    expect(queryByRole(generalTopicNode, 'button', { name: 'Delete Topic' })).not.toBeInTheDocument();
    expect(generalTopicNode.querySelector('input')).toBeInTheDocument();
  });

  test('displays expand view of additional discussion topic', async () => {
    await mockStore(legacyApiResponse);
    createComponent(additionalTopic);

    const topicCard = queryByTestId(container, '13f106c6-6735-4e84-b097-0456cff55960');
    userEvent.click(queryByLabelText(topicCard, 'Expand'));

    expect(queryByLabelText(topicCard, 'Expand')).not.toBeInTheDocument();
    expect(queryByLabelText(topicCard, 'Collapse')).toBeInTheDocument();
    expect(queryByRole(topicCard, 'button', { name: 'Delete Topic' })).toBeInTheDocument();
    expect(topicCard.querySelector('input')).toBeInTheDocument();
  });

  test('renders delete topic popup with providerName, label, helping text, a delete and a cancel button', async () => {
    await mockStore(legacyApiResponse);
    createComponent(additionalTopic);

    const topicCard = queryByTestId(container, '13f106c6-6735-4e84-b097-0456cff55960');
    userEvent.click(queryByLabelText(topicCard, 'Expand'));
    userEvent.click(queryByRole(topicCard, 'button', { name: 'Delete Topic' }));

    expect(queryAllByText(container, messages.discussionTopicDeletionLabel.defaultMessage)).toHaveLength(1);
    expect(queryByText(container, messages.discussionTopicDeletionLabel.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.discussionTopicDeletionHelp.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.discussionTopicDeletionHelp.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.deleteButton.defaultMessage)).toBeInTheDocument();
    expect(queryByText(container, messages.cancelButton.defaultMessage)).toBeInTheDocument();
  });

  test('shows help text on field focus', async () => {
    await mockStore(legacyApiResponse);
    createComponent(additionalTopic);

    const topicCard = queryByTestId(container, '13f106c6-6735-4e84-b097-0456cff55960');
    userEvent.click(queryByLabelText(topicCard, 'Expand'));
    topicCard.querySelector('input').focus();

    expect(queryByText(topicCard, messages.addTopicHelpText.defaultMessage)).toBeInTheDocument();
  });
});
Example #15
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();
    });
  });
});