it('changes clientApp when different site link clicked', () => {
    const dispatchSpy = sinon.spy(_store, 'dispatch');
    const fakeEvent = createFakeEvent({
      currentTarget: {
        getAttribute: (attribute) => {
          if (attribute === 'data-clientapp') {
            return CLIENT_APP_ANDROID;
          }
          if (attribute === 'href') {
            return `/en-US/${CLIENT_APP_ANDROID}/`;
          }

          return undefined;
        },
      },
    });
    const getAttributeSpy = sinon.spy(fakeEvent.currentTarget, 'getAttribute');
    const fakeHistory = createFakeHistory();

    _store.dispatch(setClientApp(CLIENT_APP_FIREFOX));

    const root = render({ history: fakeHistory });

    root.find(`.SectionLinks-clientApp-android`).simulate('click', fakeEvent);

    sinon.assert.called(fakeEvent.preventDefault);
    sinon.assert.calledWith(getAttributeSpy, 'data-clientapp');
    sinon.assert.calledWith(getAttributeSpy, 'href');
    sinon.assert.calledWith(dispatchSpy, setClientApp(CLIENT_APP_ANDROID));
    sinon.assert.calledWith(fakeHistory.push, `/en-US/${CLIENT_APP_ANDROID}/`);
  });
  it('maps state to props', () => {
    const { store } = createStore();
    store.dispatch(setClientApp('android'));
    store.dispatch(setLang('fr'));
    store.dispatch(categoriesLoad(categoriesResponse));

    const props = mapStateToProps(store.getState(), {
      params: { visibleAddonType: 'extensions' },
    });

    expect(props).toEqual({
      addonType: ADDON_TYPE_EXTENSION,
      categories: {
        Games: {
          application: 'android',
          name: 'Games',
          slug: 'Games',
          type: ADDON_TYPE_EXTENSION,
        },
        travel: {
          application: 'android',
          name: 'Travel',
          slug: 'travel',
          type: ADDON_TYPE_EXTENSION,
        },
      },
      clientApp: 'android',
      error: false,
      loading: false,
    });
  });
Example #3
0
 it('stores the clientApp', () => {
   const existingState = { bar: 'baz' };
   const clientApp = 'firefox';
   assert.deepEqual(
     api(existingState, actions.setClientApp(clientApp)),
     { ...existingState, clientApp });
 });
 it('stores the clientApp', () => {
   const existingState = { bar: 'baz' };
   const clientApp = 'firefox';
   expect(api(existingState, actions.setClientApp(clientApp))).toEqual({
     ...existingState,
     clientApp,
   });
 });
  it('renders a link to the Language Tools page for non-Android clients', () => {
    _store.dispatch(setClientApp(CLIENT_APP_FIREFOX));
    const root = render({ viewContext: ADDON_TYPE_EXTENSION });

    expect(
      root.find('.SectionLinks-dropdownlink').findWhere((element) => {
        return element.prop('to') === '/language-tools/';
      }),
    ).toHaveProp('children', 'Dictionaries & Language Packs');
  });
  it('hides link to the Language Tools page on Android clients', () => {
    _store.dispatch(setClientApp(CLIENT_APP_ANDROID));
    const root = render({ viewContext: ADDON_TYPE_EXTENSION });

    expect(
      root.find('.SectionLinks-dropdownlink').findWhere((element) => {
        return element.prop('to') === '/language-tools/';
      }),
    ).toHaveLength(0);
  });
  it('renders Language Tools active when viewContext is languageTools', () => {
    _store.dispatch(setClientApp(CLIENT_APP_FIREFOX));

    _store.dispatch(setViewContext(VIEW_CONTEXT_LANGUAGE_TOOLS));
    const root = render();

    expect(
      root.find('.SectionLinks-dropdownlink--active').children(),
    ).toIncludeText('Dictionaries & Language Packs');
  });
Example #8
0
  function renderHeader({ ...props }) {
    const { store } = createStore();
    store.dispatch(setClientApp('android'));
    store.dispatch(setLang('en-GB'));
    const fakeI18n = getFakeI18nInst();

    return findRenderedComponentWithType(renderIntoDocument(
      <Provider store={store}>
        <I18nProvider i18n={fakeI18n}>
          <HeaderBase i18n={fakeI18n} {...props} />
        </I18nProvider>
      </Provider>
    ), HeaderBase);
  }
Example #9
0
export function dispatchClientMetadata({
  store = createStore().store,
  clientApp = 'android',
  lang = 'en-US',
  userAgent = sampleUserAgent,
} = {}) {
  store.dispatch(setClientApp(clientApp));
  store.dispatch(setLang(lang));
  store.dispatch(setUserAgent(userAgent));

  return {
    store,
    state: store.getState(),
  };
}
  it('shows Android name and hides link in header on Android', () => {
    _store.dispatch(setClientApp(CLIENT_APP_ANDROID));
    const root = render();

    expect(
      root
        .find('.SectionLinks-subheader')
        .at(0)
        .children(),
    ).toIncludeText('for Android');
    expect(
      root.find(`.SectionLinks-clientApp-${CLIENT_APP_ANDROID}`),
    ).toHaveLength(0);
    expect(
      root.find(`.SectionLinks-clientApp-${CLIENT_APP_FIREFOX}`),
    ).toHaveLength(1);
  });
  beforeEach(() => {
    errorHandler = createStubErrorHandler();
    store = createStore().store;
    store.dispatch(setClientApp('firefox'));
    store.dispatch(setLang('en-US'));

    const state = store.getState();
    initialState = {
      api: state.api,
      categories: state.categories,
    };

    sagaTester = new SagaTester({
      initialState,
      reducers: { api: apiReducer, categories: categoriesReducer },
    });
    sagaTester.start(categoriesSaga);
  });
  function render({ ...props }) {
    const { store } = createStore();
    store.dispatch(setClientApp('android'));
    store.dispatch(setLang('fr'));
    store.dispatch(categoriesLoad(categoriesResponse));

    const { categories } = store.getState().categories;
    const baseProps = {
      clientApp: store.getState().api.clientApp,
      categories: categories[CLIENT_APP_ANDROID][ADDON_TYPE_EXTENSION],
      dispatch: sinon.stub(),
    };

    return findRenderedComponentWithType(renderIntoDocument(
      <Provider store={store}>
        <CategoriesBase i18n={getFakeI18nInst()} {...baseProps} {...props} />
      </Provider>
    ), CategoriesBase);
  }
 expect(() => actions.setClientApp('')).toThrowError(/cannot be falsey/);
 it('creates the SET_CLIENT_APP action', () => {
   expect(actions.setClientApp('firefox')).toEqual({
     type: 'SET_CLIENT_APP',
     payload: { clientApp: 'firefox' },
   });
 });
Example #15
0
    match({ location: req.url, routes }, (
      err, redirectLocation, renderProps
    ) => {
      cookie.plugToRequest(req, res);

      if (err) {
        log.error({ err, req });
        return showErrorPage(res, 500);
      }

      if (!renderProps) {
        return showErrorPage(res, 404);
      }

      const store = createStore();
      const token = cookie.load(config.get('cookieName'));
      if (token) {
        store.dispatch(setJwt(token));
      }
      // Get SRI for deployed services only.
      const sriData = (isDeployed) ? JSON.parse(
        fs.readFileSync(path.join(config.get('basePath'), 'dist/sri.json'))
      ) : {};

      // Check the lang supplied by res.locals.lang for validity
      // or fall-back to the default.
      const lang = isValidLang(res.locals.lang) ?
        res.locals.lang : config.get('defaultLang');
      const dir = getDirection(lang);
      const locale = langToLocale(lang);
      store.dispatch(setLang(lang));
      if (res.locals.clientApp) {
        store.dispatch(setClientApp(res.locals.clientApp));
      } else {
        log.warn(`No clientApp for this URL: ${req.url}`);
      }

      function hydrateOnClient(props = {}) {
        const pageProps = {
          appName: appInstanceName,
          assets: webpackIsomorphicTools.assets(),
          htmlLang: lang,
          htmlDir: dir,
          includeSri: isDeployed,
          noScriptStyles,
          sriData,
          store,
          trackingEnabled: convertBoolean(config.get('trackingEnabled')),
          ...props,
        };

        const HTML = ReactDOM.renderToString(
          <ServerHtml {...pageProps} />);
        res.send(`<!DOCTYPE html>\n${HTML}`);
      }

      // Set disableSSR to true to debug
      // client-side-only render.
      if (config.get('disableSSR') === true) {
        return Promise.resolve(hydrateOnClient());
      }

      return loadOnServer({ ...renderProps, store })
        .then(() => {
          // eslint-disable-next-line global-require
          let i18nData = {};
          try {
            if (locale !== langToLocale(config.get('defaultLang'))) {
              // eslint-disable-next-line global-require, import/no-dynamic-require
              i18nData = require(
                `../../locale/${locale}/${appInstanceName}.js`);
            }
          } catch (e) {
            log.info(
              `Locale JSON not found or required for locale: "${locale}"`);
            log.info(
              `Falling back to default lang: "${config.get('defaultLang')}".`);
          }
          const i18n = makeI18n(i18nData, lang);

          const InitialComponent = (
            <I18nProvider i18n={i18n}>
              <Provider store={store} key="provider">
                <ReduxAsyncConnect {...renderProps} />
              </Provider>
            </I18nProvider>
          );

          const asyncConnectLoadState = store.getState().reduxAsyncConnect.loadState || {};
          const reduxResult = getReduxConnectError(asyncConnectLoadState);
          if (reduxResult.status) {
            return showErrorPage(res, reduxResult.status);
          }

          return hydrateOnClient({ component: InitialComponent });
        })
        .catch((error) => {
          log.error({ err: error });
          return showErrorPage(res, 500);
        });
    });
Example #16
0
 beforeEach(() => {
   store = createStore().store;
   store.dispatch(setClientApp('android'));
   store.dispatch(setLang('fr'));
 });