it('renders a LanguageToolList', () => {
      const { store } = dispatchClientMetadata({ lang: 'en-GB' });
      store.dispatch(loadLanguageTools({ languageTools }));

      const allLanguageTools = getAllLanguageTools(store.getState());

      const languageToolsInYourLocale = allLanguageTools.filter(
        (languageTool) => {
          return languageTool.target_locale === store.getState().api.lang;
        },
      );
      const dictionaries = languageToolsInYourLocale.filter((languageTool) => {
        return languageTool.type === ADDON_TYPE_DICT;
      });
      const languagePacks = languageToolsInYourLocale.filter((languageTool) => {
        return languageTool.type === ADDON_TYPE_LANG;
      });

      const dictionaryList = shallow(
        <LanguageToolList languageTools={dictionaries} />,
      );
      const languagePackList = shallow(
        <LanguageToolList languageTools={languagePacks} />,
      );

      expect(dictionaryList.find('.LanguageTools-addon-list')).toHaveLength(1);
      expect(languagePackList.find('.LanguageTools-addon-list')).toHaveLength(
        1,
      );
      expect(dictionaryList.find('li')).toHaveLength(1);
      expect(languagePackList.find('li')).toHaveLength(2);
    });
  it('renders an error when an API error is thrown', () => {
    const { store } = dispatchClientMetadata();
    const authorUsernames = ['some', 'authors'];

    const errorHandler = new ErrorHandler({
      id: 'some-id',
      dispatch: store.dispatch,
    });
    errorHandler.handle(
      createApiError({
        response: { status: 404 },
        apiURL: 'https://some/api/endpoint',
        jsonResponse: { message: 'not found' },
      }),
    );

    const root = render({
      addonType: ADDON_TYPE_EXTENSION,
      authorUsernames,
      errorHandler,
      store,
    });

    expect(root.find(ErrorList)).toHaveLength(1);
  });
    it('allows abuse reports for multiple add-ons', () => {
      const { store } = dispatchClientMetadata();

      store.dispatch(
        loadAddonAbuseReport(
          createFakeAddonAbuseReport({
            addon: { ...fakeAddon, slug: 'some-addon' },
            message: 'This add-on is malwaré.',
            reporter: null,
          }),
        ),
      );
      store.dispatch(
        loadAddonAbuseReport(
          createFakeAddonAbuseReport({
            addon: { ...fakeAddon, slug: 'another-addon' },
            message: 'The add-on is boring',
            reporter: null,
          }),
        ),
      );

      expect(store.getState().abuse).toMatchObject({
        bySlug: {
          'another-addon': {
            message: 'The add-on is boring',
          },
          'some-addon': {
            message: 'This add-on is malwaré.',
          },
        },
      });
    });
  it('populates the edit form with collection data', () => {
    const clientApp = CLIENT_APP_FIREFOX;
    const newLang = 'de';
    const username = '******';
    const localStore = dispatchClientMetadata({ clientApp, lang: newLang })
      .store;
    dispatchSignInActions({
      lang: newLang,
      store: localStore,
      userProps: { username },
    });
    const collection = createInternalCollection({
      detail: createFakeCollectionDetail({
        name: 'OG name',
        description: 'OG description',
        slug: 'the-slug',
        authorUsername: username,
      }),
    });
    const root = render({ collection, store: localStore });

    const expectedUrlPrefix = `${apiHost}/${newLang}/${clientApp}/collections/${username}/`;
    expect(root.find('#collectionName')).toHaveProp('value', collection.name);
    expect(root.find('#collectionDescription')).toHaveProp(
      'value',
      collection.description,
    );
    expect(root.find('#collectionSlug')).toHaveProp('value', collection.slug);
    expect(root.find('#collectionUrlPrefix')).toHaveProp(
      'title',
      expectedUrlPrefix,
    );
    expect(root.find('#collectionUrlPrefix')).toIncludeText(expectedUrlPrefix);
  });
  it('only hides dismissible notices', () => {
    const { store } = dispatchClientMetadata();
    const root = render({ store, dismissible: false });
    setUIState({ root, store, change: { wasDismissed: true } });

    expect(root.find('.Notice')).toHaveLength(1);
  });
  it('passes review=null if no user is signed in', () => {
    const { store } = dispatchClientMetadata();
    const root = render({ store });

    // This does not trigger a loading state.
    expect(root.find(UserRating)).toHaveProp('review', null);
  });
    it('shows an empty notes form when the leave a note button is clicked', () => {
      const { store } = dispatchClientMetadata();
      const root = render({ store });

      expect(root.find('.EditableCollectionAddon-notes')).toHaveLength(0);

      root
        .find('.EditableCollectionAddon-leaveNote-button')
        .simulate('click', createFakeEvent());
      applyUIStateChanges({ root, store });

      expect(root.find('.EditableCollectionAddon-notes')).toHaveLength(1);

      const notesForm = root.find('.EditableCollectionAddon-notes-form');
      expect(notesForm).toHaveLength(1);
      expect(notesForm).toHaveProp('microButtons', true);
      expect(notesForm).toHaveProp('onDelete', null);
      expect(notesForm).toHaveProp(
        'onDismiss',
        root.instance().onDismissNoteForm,
      );
      expect(notesForm).toHaveProp('onSubmit', root.instance().onSaveNote);
      expect(notesForm).toHaveProp(
        'placeholder',
        'Add a comment about this add-on.',
      );
      expect(notesForm).toHaveProp('submitButtonText', 'Save');
      expect(notesForm).toHaveProp('text', null);

      // The read-only portion should not be shown.
      expect(
        root.find('.EditableCollectionAddon-notes-read-only'),
      ).toHaveLength(0);
    });
  it('renders add-ons for all variants of a short locale on a single row (only one supported language)', () => {
    // The short locale is `az` here.
    const addons = [
      createFakeLanguageTool({
        id: 1,
        name: 'Azərbaycanca (AZ) Language Pack',
        target_locale: 'az',
        type: ADDON_TYPE_LANG,
      }),
      createFakeLanguageTool({
        id: 2,
        name: 'Azerbaijani Spell Checker',
        target_locale: 'az-IR',
        type: ADDON_TYPE_DICT,
      }),
    ];

    const { store } = dispatchClientMetadata();
    store.dispatch(loadLanguageTools({ languageTools: addons }));

    const root = renderShallow({ store });

    // We expect only one row with all the add-ons in it.
    expect(root.find('.LanguageTools-table-row')).toHaveLength(1);

    expect(root.find(LanguageToolList)).toHaveLength(2);
    expect(root.find(LanguageToolList).at(0)).toHaveProp('languageTools', [
      addons[0],
    ]);
    expect(root.find(LanguageToolList).at(1)).toHaveProp('languageTools', [
      addons[1],
    ]);
  });
    it('does not fetch user collections when signed out', () => {
      dispatchClientMetadata({ store });
      const dispatchSpy = sinon.spy(store, 'dispatch');
      render();

      sinon.assert.notCalled(dispatchSpy);
    });
 const _dispatchClientMetadata = (params = {}) => {
   return dispatchClientMetadata({
     store,
     userAgent: userAgentsByPlatform.mac.firefox57,
     ...params,
   });
 };
    it('should dispatch a fetch action with `page` and `sort` parameters', () => {
      const { store } = dispatchClientMetadata();
      const dispatchSpy = sinon.spy(store, 'dispatch');
      const errorHandler = createStubErrorHandler();

      const authorUsernames = ['test2'];
      const numberOfAddons = 4;

      renderWithPagination({
        addonType: ADDON_TYPE_EXTENSION,
        authorUsernames,
        errorHandler,
        numberOfAddons,
        store,
      });

      sinon.assert.calledWith(
        dispatchSpy,
        fetchAddonsByAuthors({
          addonType: ADDON_TYPE_EXTENSION,
          authorUsernames,
          errorHandlerId: errorHandler.id,
          page: 1,
          pageSize: numberOfAddons,
          sort: SEARCH_SORT_POPULAR,
        }),
      );
    });
  it('can render an empty form for create', () => {
    const clientApp = CLIENT_APP_FIREFOX;
    const newLang = 'de';
    const username = '******';
    const localStore = dispatchClientMetadata({ clientApp, lang: newLang })
      .store;
    dispatchSignInActions({
      lang: newLang,
      store: localStore,
      userProps: { username },
    });

    const root = render({
      collection: null,
      creating: true,
      store: localStore,
    });

    const expectedUrlPrefix = `${apiHost}/${newLang}/${clientApp}/collections/${username}/`;
    expect(root.find('#collectionName')).toHaveProp('value', null);
    expect(root.find('#collectionDescription')).toHaveProp('value', null);
    expect(root.find('#collectionSlug')).toHaveProp('value', null);
    expect(root.find('#collectionUrlPrefix')).toHaveProp(
      'title',
      expectedUrlPrefix,
    );
    expect(root.find('#collectionUrlPrefix')).toIncludeText(expectedUrlPrefix);
  });
  it('should dispatch a fetch action if forAddonSlug is updated', () => {
    const { store } = dispatchClientMetadata();
    const dispatchSpy = sinon.spy(store, 'dispatch');
    const errorHandler = createStubErrorHandler();
    const numberOfAddons = 4;

    const root = render({
      addonType: ADDON_TYPE_EXTENSION,
      authorUsernames: ['test2'],
      errorHandler,
      numberOfAddons,
      store,
    });

    dispatchSpy.resetHistory();

    root.setProps({
      forAddonSlug: 'testing',
    });

    sinon.assert.calledWith(
      dispatchSpy,
      fetchAddonsByAuthors({
        addonType: ADDON_TYPE_EXTENSION,
        authorUsernames: ['test2'],
        errorHandlerId: errorHandler.id,
        forAddonSlug: 'testing',
        pageSize: numberOfAddons,
      }),
    );
  });
  function renderAddonsWithType({
    addonType,
    showMore,
    multipleAuthors = false,
    numberOfAddons = 6,
    count = null,
    ...otherProps
  } = {}) {
    const authorUsernames = multipleAuthors
      ? [fakeAuthorOne.username, fakeAuthorTwo.username]
      : [fakeAuthorOne.username];
    const { store } = dispatchClientMetadata();
    const errorHandler = createStubErrorHandler();

    store.dispatch(
      addonsWithAuthorsOfType({
        addonType,
        count,
        multipleAuthors,
      }),
    );

    return render({
      addonType,
      showMore,
      authorUsernames,
      errorHandler,
      numberOfAddons,
      store,
      ...otherProps,
    });
  }
  it('does not display a collection shelf if there is no collection in state', () => {
    const { store } = dispatchClientMetadata();

    const addons = [{ ...fakeAddon, slug: 'addon' }];

    const collections = [null, null, null];
    const featuredExtensions = createAddonsApiResult(addons);
    const featuredThemes = createAddonsApiResult([]);
    const popularExtensions = createAddonsApiResult(addons);

    store.dispatch(
      loadHomeAddons({
        collections,
        featuredExtensions,
        featuredThemes,
        popularExtensions,
      }),
    );

    const root = render({ store });
    const shelves = root.find(LandingAddonsCard);

    const collectionShelves = shelves.find('.Home-FeaturedCollection');
    expect(collectionShelves).toHaveLength(0);
  });
  it('it renders categories if they exist', () => {
    const categoriesResponse = {
      result: [
        {
          application: 'android',
          name: 'Games',
          slug: 'Games',
          type: ADDON_TYPE_EXTENSION,
        },
        {
          application: 'android',
          name: 'Travel',
          slug: 'travel',
          type: ADDON_TYPE_EXTENSION,
        },
      ],
    };

    const { store } = dispatchClientMetadata();
    store.dispatch(categoriesLoad(categoriesResponse));
    const { categories } = mapStateToProps(store.getState());

    const root = render({
      addonType: ADDON_TYPE_EXTENSION,
      categories,
    });

    expect(root.find('.CategoriesHeader-list').childAt(0).find(Button))
      .toHaveProp('children', 'Games');
    expect(root.find('.CategoriesHeader-list').childAt(1).find(Button))
      .toHaveProp('children', 'Travel');
  });
  it('does not dispatch fetchUserCollections if there is no user', () => {
    const { store } = dispatchClientMetadata();
    const fakeDispatch = sinon.spy(store, 'dispatch');

    renderComponent({ store });

    sinon.assert.notCalled(fakeDispatch);
  });
  function render(props = {}) {
    const { store } = dispatchClientMetadata();

    return shallowUntilTarget(
      <DownloadFirefoxButton i18n={fakeI18n()} store={store} {...props} />,
      DownloadFirefoxButtonBase,
    );
  }
  it('renders nothing if the browser is Firefox for iOS', () => {
    const { store } = dispatchClientMetadata({
      userAgent: userAgents.firefoxIOS[0],
    });
    const root = render({ store });

    expect(root.find('.DownloadFirefoxButton')).toHaveLength(0);
  });
 const getProps = (customProps = {}) => {
   return {
     i18n: fakeI18n(),
     pathname: '/search/',
     store: dispatchClientMetadata().store,
     ...customProps,
   };
 };
    it('returns an array of add-ons', () => {
      const { store } = dispatchClientMetadata();
      store.dispatch(loadAddons(createFetchAddonResult(fakeAddon).entities));

      expect(getAllAddons(store.getState())).toEqual([
        createInternalAddon(fakeAddon),
      ]);
    });
    it('returns an add-on by guid', () => {
      const { store } = dispatchClientMetadata();
      store.dispatch(loadAddons(createFetchAddonResult(fakeAddon).entities));

      expect(getAddonByGUID(store.getState(), fakeAddon.guid)).toEqual(
        createInternalAddon(fakeAddon),
      );
    });
 it('hides when you click the background', () => {
   const { store } = dispatchClientMetadata();
   const root = render({ visibleOnLoad: true, store });
   const btn = root.find('.Overlay-background');
   btn.simulate('click', createFakeEvent());
   applyUIStateChanges({ root, store });
   expect(root).not.toHaveClassName('Overlay--visible');
 });
    it('configures UserRating when signed out', async () => {
      const root = renderInline({ store: dispatchClientMetadata().store });

      const rating = root.find(UserRating);
      expect(rating).toHaveLength(1);
      expect(rating).toHaveProp('readOnly', true);
      expect(rating).toHaveProp('review', null);
    });
 beforeEach(() => {
   callApiMock = sinon.stub(coreApi, 'callApi');
   fakeConfig = getFakeConfig({
     discoParamsToUse: ['clientId', 'platform'],
   });
   const { store } = createStore();
   apiState = dispatchClientMetadata({ store }).state.api;
 });
  it('renders LoadingText if language tools are empty', () => {
    const { store } = dispatchClientMetadata();
    store.dispatch(loadLanguageTools({ languageTools: [] }));

    const root = renderShallow({ store });

    expect(root.find(LoadingText)).not.toHaveLength(0);
  });
  it('does not render languages we know of but do not have languages for', () => {
    const { store } = dispatchClientMetadata();
    store.dispatch(loadLanguageTools({ languageTools }));

    const root = renderShallow({ store });

    expect(root.find('.LanguageTools-lang-es')).toHaveLength(0);
  });
  it('renders an HTML title', () => {
    const { store } = dispatchClientMetadata({ lang: 'pt-BR' });
    store.dispatch(loadLanguageTools({ languageTools }));

    const root = renderShallow({ store });

    expect(root.find('title')).toHaveText('Dictionaries and Language Packs');
  });
 const getProps = ({ ...props } = {}) => {
   return {
     id: 'Overlay',
     className: 'Overlay',
     store: dispatchClientMetadata().store,
     ...props,
   };
 };
  it('omits "language tools in your locale" section if none available', () => {
    const { store } = dispatchClientMetadata({ lang: 'pt-BR' });
    store.dispatch(loadLanguageTools({ languageTools }));

    const root = renderShallow({ store });

    expect(root.find('.LanguageTools-in-your-locale')).toHaveLength(0);
  });