<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
/**
 * External dependencies
 */
import React, { useEffect } from 'react';
import propTypes from 'prop-types';
import styled from 'styled-components';

/**
 * Internal dependencies
 */
import loadStylesheet from '../../../utils/loadStylesheet';
import { GOOGLE_MENU_FONT_URL } from '../../../app/font';
import {
  ANIMATION_EFFECTS,
  DIRECTION,
  ROTATION,
} from '../../../../animation/constants';
import {
  GRID_ITEM_HEIGHT,
  PANEL_WIDTH,
  DropAnimation,
  FadeInAnimation,
  FlyInLeftAnimation,
  FlyInRightAnimation,
  FlyInTopAnimation,
  FlyInBottomAnimation,
  PulseAnimation,
  RotateInLeftAnimation,
  RotateInRightAnimation,
  TwirlInAnimation,
  WhooshInLeftAnimation,
  WhooshInRightAnimation,
  PanRightAnimation,
  PanBottomAnimation,
  PanTopAnimation,
  PanLeftAnimation,
  ZoomInAnimation,
  ZoomOutAnimation,
  BaseAnimationCell,
} from './effectChooserElements';

const Container = styled.div`
  background: black;
  width: ${PANEL_WIDTH}px;
`;

const ContentWrapper = styled.div`
  display: inline-block;
`;

const GridItem = styled.button.attrs({ role: 'listitem' })`
  border: none;
  background: #333;
  border-radius: 4px;
  height: ${GRID_ITEM_HEIGHT}px;
  position: relative;
  overflow: hidden;
  font-family: 'Teko', sans-serif;
  font-size: 20px;
  color: white;
  text-transform: uppercase;

  &:hover {
    ${BaseAnimationCell} {
      display: inline-block;
    }

    ${ContentWrapper} {
      display: none;
    }
  }
`;

const Grid = styled.div.attrs({ role: 'list' })`
  display: grid;
  justify-content: center;
  gap: 15px 3px;
  grid-template-columns: repeat(4, 58px);
  padding: 15px;
  position: relative;
`;

const GridItemFullRow = styled(GridItem)`
  grid-column-start: span 4;
`;

const GridItemHalfRow = styled(GridItem)`
  grid-column-start: span 2;
`;

export default function EffectChooser({ onAnimationSelected }) {
  useEffect(() => {
    loadStylesheet(`${GOOGLE_MENU_FONT_URL}?family=Teko`).catch(function () {});
  });

  return (
    <Container>
      <Grid>
        <GridItemFullRow
          aria-label={__('Drop Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({ animation: ANIMATION_EFFECTS.DROP.value })
          }
        >
          <ContentWrapper>{__('Drop', 'web-stories')}</ContentWrapper>
          <DropAnimation>{__('Drop', 'web-stories')}</DropAnimation>
        </GridItemFullRow>
        <GridItemFullRow
          aria-label={__('Fade In Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({ animation: ANIMATION_EFFECTS.FADE_IN.value })
          }
        >
          <ContentWrapper>{__('Fade in', 'web-stories')}</ContentWrapper>
          <FadeInAnimation>{__('Fade in', 'web-stories')}</FadeInAnimation>
        </GridItemFullRow>
        <GridItem
          aria-label={__('Fly In from Left Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.LEFT_TO_RIGHT,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInLeftAnimation>{__('Fly in', 'web-stories')}</FlyInLeftAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Fly In from Top Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.TOP_TO_BOTTOM,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInTopAnimation>{__('Fly in', 'web-stories')}</FlyInTopAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Fly In from Bottom Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.BOTTOM_TO_TOP,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInBottomAnimation>
            {__('Fly in', 'web-stories')}
          </FlyInBottomAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Fly In from Right Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.RIGHT_TO_LEFT,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInRightAnimation>
            {__('Fly in', 'web-stories')}
          </FlyInRightAnimation>
        </GridItem>
        <GridItemFullRow
          aria-label={__('Pulse Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PULSE.value,
            })
          }
        >
          <ContentWrapper>{__('Pulse', 'web-stories')}</ContentWrapper>
          <PulseAnimation>{__('Pulse', 'web-stories')}</PulseAnimation>
        </GridItemFullRow>
        <GridItemHalfRow
          aria-label={__('Rotate In Counter Clockwise Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ROTATE_IN.value,
              rotation: ROTATION.COUNTER_CLOCKWISE,
            })
          }
        >
          <ContentWrapper>{__('Rotate', 'web-stories')}</ContentWrapper>
          <RotateInLeftAnimation>
            {__('Rotate', 'web-stories')}
          </RotateInLeftAnimation>
        </GridItemHalfRow>
        <GridItemHalfRow
          aria-label={__('Rotate In Clockwise Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ROTATE_IN.value,
              rotation: ROTATION.CLOCKWISE,
            })
          }
        >
          <ContentWrapper>{__('Rotate', 'web-stories')}</ContentWrapper>
          <RotateInRightAnimation>
            {__('Rotate', 'web-stories')}
          </RotateInRightAnimation>
        </GridItemHalfRow>
        <GridItemFullRow
          aria-label={__('Twirl In Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.TWIRL_IN.value,
            })
          }
        >
          <ContentWrapper>{__('Twirl In', 'web-stories')}</ContentWrapper>
          <TwirlInAnimation>{__('Twirl In', 'web-stories')}</TwirlInAnimation>
        </GridItemFullRow>
        <GridItemHalfRow
          aria-label={__('Whoosh In from Left Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.WHOOSH_IN.value,
              direction: DIRECTION.LEFT_TO_RIGHT,
            })
          }
        >
          <ContentWrapper>{__('Woosh In', 'web-stories')}</ContentWrapper>
          <WhooshInLeftAnimation>
            {__('Woosh In', 'web-stories')}
          </WhooshInLeftAnimation>
        </GridItemHalfRow>
        <GridItemHalfRow
          aria-label={__('Whoosh In from Right Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.WHOOSH_IN.value,
              direction: DIRECTION.RIGHT_TO_LEFT,
            })
          }
        >
          <WhooshInRightAnimation>
            {__('Woosh In', 'web-stories')}
          </WhooshInRightAnimation>
          <ContentWrapper>{__('Woosh In', 'web-stories')}</ContentWrapper>
        </GridItemHalfRow>
        <GridItem
          aria-label={__('Pan from Left Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.LEFT_TO_RIGHT,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>
          <PanLeftAnimation>{__('Pan', 'web-stories')}</PanLeftAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Pan from Top Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.TOP_TO_BOTTOM,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>
          <PanTopAnimation>{__('Pan', 'web-stories')}</PanTopAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Pan from Bottom Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.BOTTOM_TO_TOP,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>{' '}
          <PanBottomAnimation>{__('Pan', 'web-stories')}</PanBottomAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Pan from Right Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.RIGHT_TO_LEFT,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>
          <PanRightAnimation>{__('Pan', 'web-stories')}</PanRightAnimation>
        </GridItem>
        <GridItemHalfRow
          aria-label={__('Zoom In Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ZOOM.value,
              defaultValue: 0,
            })
          }
        >
          <ContentWrapper>{__('Zoom In', 'web-stories')}</ContentWrapper>
          <ZoomInAnimation>{__('Zoom In', 'web-stories')}</ZoomInAnimation>
        </GridItemHalfRow>
        <GridItemHalfRow
          aria-label={__('Zoom Out Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ZOOM.value,
              defaultValue: 1,
            })
          }
        >
          <ZoomOutAnimation>{__('Zoom Out', 'web-stories')}</ZoomOutAnimation>
          <ContentWrapper>{__('Zoom Out', 'web-stories')}</ContentWrapper>
        </GridItemHalfRow>
      </Grid>
    </Container>
  );
}

EffectChooser.propTypes = {
  onAnimationSelected: propTypes.func.isRequired,
};
<commit_msg>Perform animation on focus.<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
/**
 * External dependencies
 */
import React, { useEffect } from 'react';
import propTypes from 'prop-types';
import styled from 'styled-components';

/**
 * Internal dependencies
 */
import loadStylesheet from '../../../utils/loadStylesheet';
import { GOOGLE_MENU_FONT_URL } from '../../../app/font';
import {
  ANIMATION_EFFECTS,
  DIRECTION,
  ROTATION,
} from '../../../../animation/constants';
import {
  GRID_ITEM_HEIGHT,
  PANEL_WIDTH,
  DropAnimation,
  FadeInAnimation,
  FlyInLeftAnimation,
  FlyInRightAnimation,
  FlyInTopAnimation,
  FlyInBottomAnimation,
  PulseAnimation,
  RotateInLeftAnimation,
  RotateInRightAnimation,
  TwirlInAnimation,
  WhooshInLeftAnimation,
  WhooshInRightAnimation,
  PanRightAnimation,
  PanBottomAnimation,
  PanTopAnimation,
  PanLeftAnimation,
  ZoomInAnimation,
  ZoomOutAnimation,
  BaseAnimationCell,
} from './effectChooserElements';

const Container = styled.div`
  background: black;
  width: ${PANEL_WIDTH}px;
`;

const ContentWrapper = styled.div`
  display: inline-block;
`;

const GridItem = styled.button.attrs({ role: 'listitem' })`
  border: none;
  background: #333;
  border-radius: 4px;
  height: ${GRID_ITEM_HEIGHT}px;
  position: relative;
  overflow: hidden;
  font-family: 'Teko', sans-serif;
  font-size: 20px;
  color: white;
  text-transform: uppercase;

  &:hover,
  &:focus {
    ${BaseAnimationCell} {
      display: inline-block;
    }

    ${ContentWrapper} {
      display: none;
    }
  }
`;

const Grid = styled.div.attrs({ role: 'list' })`
  display: grid;
  justify-content: center;
  gap: 15px 3px;
  grid-template-columns: repeat(4, 58px);
  padding: 15px;
  position: relative;
`;

const GridItemFullRow = styled(GridItem)`
  grid-column-start: span 4;
`;

const GridItemHalfRow = styled(GridItem)`
  grid-column-start: span 2;
`;

export default function EffectChooser({ onAnimationSelected }) {
  useEffect(() => {
    loadStylesheet(`${GOOGLE_MENU_FONT_URL}?family=Teko`).catch(function () {});
  });

  return (
    <Container>
      <Grid>
        <GridItemFullRow
          aria-label={__('Drop Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({ animation: ANIMATION_EFFECTS.DROP.value })
          }
        >
          <ContentWrapper>{__('Drop', 'web-stories')}</ContentWrapper>
          <DropAnimation>{__('Drop', 'web-stories')}</DropAnimation>
        </GridItemFullRow>
        <GridItemFullRow
          aria-label={__('Fade In Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({ animation: ANIMATION_EFFECTS.FADE_IN.value })
          }
        >
          <ContentWrapper>{__('Fade in', 'web-stories')}</ContentWrapper>
          <FadeInAnimation>{__('Fade in', 'web-stories')}</FadeInAnimation>
        </GridItemFullRow>
        <GridItem
          aria-label={__('Fly In from Left Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.LEFT_TO_RIGHT,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInLeftAnimation>{__('Fly in', 'web-stories')}</FlyInLeftAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Fly In from Top Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.TOP_TO_BOTTOM,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInTopAnimation>{__('Fly in', 'web-stories')}</FlyInTopAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Fly In from Bottom Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.BOTTOM_TO_TOP,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInBottomAnimation>
            {__('Fly in', 'web-stories')}
          </FlyInBottomAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Fly In from Right Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.FLY_IN.value,
              direction: DIRECTION.RIGHT_TO_LEFT,
            })
          }
        >
          <ContentWrapper>{__('Fly in', 'web-stories')}</ContentWrapper>
          <FlyInRightAnimation>
            {__('Fly in', 'web-stories')}
          </FlyInRightAnimation>
        </GridItem>
        <GridItemFullRow
          aria-label={__('Pulse Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PULSE.value,
            })
          }
        >
          <ContentWrapper>{__('Pulse', 'web-stories')}</ContentWrapper>
          <PulseAnimation>{__('Pulse', 'web-stories')}</PulseAnimation>
        </GridItemFullRow>
        <GridItemHalfRow
          aria-label={__('Rotate In Counter Clockwise Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ROTATE_IN.value,
              rotation: ROTATION.COUNTER_CLOCKWISE,
            })
          }
        >
          <ContentWrapper>{__('Rotate', 'web-stories')}</ContentWrapper>
          <RotateInLeftAnimation>
            {__('Rotate', 'web-stories')}
          </RotateInLeftAnimation>
        </GridItemHalfRow>
        <GridItemHalfRow
          aria-label={__('Rotate In Clockwise Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ROTATE_IN.value,
              rotation: ROTATION.CLOCKWISE,
            })
          }
        >
          <ContentWrapper>{__('Rotate', 'web-stories')}</ContentWrapper>
          <RotateInRightAnimation>
            {__('Rotate', 'web-stories')}
          </RotateInRightAnimation>
        </GridItemHalfRow>
        <GridItemFullRow
          aria-label={__('Twirl In Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.TWIRL_IN.value,
            })
          }
        >
          <ContentWrapper>{__('Twirl In', 'web-stories')}</ContentWrapper>
          <TwirlInAnimation>{__('Twirl In', 'web-stories')}</TwirlInAnimation>
        </GridItemFullRow>
        <GridItemHalfRow
          aria-label={__('Whoosh In from Left Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.WHOOSH_IN.value,
              direction: DIRECTION.LEFT_TO_RIGHT,
            })
          }
        >
          <ContentWrapper>{__('Woosh In', 'web-stories')}</ContentWrapper>
          <WhooshInLeftAnimation>
            {__('Woosh In', 'web-stories')}
          </WhooshInLeftAnimation>
        </GridItemHalfRow>
        <GridItemHalfRow
          aria-label={__('Whoosh In from Right Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.WHOOSH_IN.value,
              direction: DIRECTION.RIGHT_TO_LEFT,
            })
          }
        >
          <WhooshInRightAnimation>
            {__('Woosh In', 'web-stories')}
          </WhooshInRightAnimation>
          <ContentWrapper>{__('Woosh In', 'web-stories')}</ContentWrapper>
        </GridItemHalfRow>
        <GridItem
          aria-label={__('Pan from Left Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.LEFT_TO_RIGHT,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>
          <PanLeftAnimation>{__('Pan', 'web-stories')}</PanLeftAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Pan from Top Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.TOP_TO_BOTTOM,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>
          <PanTopAnimation>{__('Pan', 'web-stories')}</PanTopAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Pan from Bottom Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.BOTTOM_TO_TOP,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>{' '}
          <PanBottomAnimation>{__('Pan', 'web-stories')}</PanBottomAnimation>
        </GridItem>
        <GridItem
          aria-label={__('Pan from Right Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.PAN.value,
              direction: DIRECTION.RIGHT_TO_LEFT,
            })
          }
        >
          <ContentWrapper>{__('Pan', 'web-stories')}</ContentWrapper>
          <PanRightAnimation>{__('Pan', 'web-stories')}</PanRightAnimation>
        </GridItem>
        <GridItemHalfRow
          aria-label={__('Zoom In Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ZOOM.value,
              defaultValue: 0,
            })
          }
        >
          <ContentWrapper>{__('Zoom In', 'web-stories')}</ContentWrapper>
          <ZoomInAnimation>{__('Zoom In', 'web-stories')}</ZoomInAnimation>
        </GridItemHalfRow>
        <GridItemHalfRow
          aria-label={__('Zoom Out Effect', 'web-stories')}
          onClick={() =>
            onAnimationSelected({
              animation: ANIMATION_EFFECTS.ZOOM.value,
              defaultValue: 1,
            })
          }
        >
          <ZoomOutAnimation>{__('Zoom Out', 'web-stories')}</ZoomOutAnimation>
          <ContentWrapper>{__('Zoom Out', 'web-stories')}</ContentWrapper>
        </GridItemHalfRow>
      </Grid>
    </Container>
  );
}

EffectChooser.propTypes = {
  onAnimationSelected: propTypes.func.isRequired,
};
<|endoftext|>


<commit_before><commit_msg>Ajuste legales siempre visibles<commit_after><|endoftext|>


<commit_before>/**
 * External dependencies
 */
import castArray from 'lodash/castArray';
import mapValues from 'lodash/mapValues';
import fetchMock from 'fetch-mock';

/**
 * WordPress dependencies
 */
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
 * Internal dependencies
 */
import * as coreSite from '../../assets/js/googlesitekit/datastore/site';
import * as coreUser from '../../assets/js/googlesitekit/datastore/user';
import * as coreForms from '../../assets/js/googlesitekit/datastore/forms';
import * as coreLocation from '../../assets/js/googlesitekit/datastore/location';
import * as coreModules from '../../assets/js/googlesitekit/modules';
import * as coreWidgets from '../../assets/js/googlesitekit/widgets';
import * as modulesAdSense from '../../assets/js/modules/adsense';
import * as modulesAnalytics from '../../assets/js/modules/analytics';
import * as modulesPageSpeedInsights from '../../assets/js/modules/pagespeed-insights';
import * as modulesSearchConsole from '../../assets/js/modules/search-console';
import * as modulesTagManager from '../../assets/js/modules/tagmanager';
import * as modulesOptimize from '../../assets/js/modules/optimize';
import { STORE_NAME as CORE_SITE } from '../../assets/js/googlesitekit/datastore/site/constants';
import { STORE_NAME as CORE_USER } from '../../assets/js/googlesitekit/datastore/user/constants';
import { STORE_NAME as CORE_MODULES } from '../../assets/js/googlesitekit/modules/datastore/constants';
import coreModulesFixture from '../../assets/js/googlesitekit/modules/datastore/fixtures.json';

const allCoreStores = [
	coreForms,
	coreLocation,
	coreModules,
	coreSite,
	coreUser,
	coreWidgets,
];
const allModules = [
	modulesAdSense,
	modulesAnalytics,
	modulesOptimize,
	modulesPageSpeedInsights,
	modulesSearchConsole,
	modulesTagManager,
];

/**
 * Creates a registry with all available stores.
 *
 * @since 1.5.0
 * @private
 *
 * @return {wp.data.registry} Registry with all available stores registered.
 */
export const createTestRegistry = () => {
	const registry = createRegistry();

	// Register all available stores on the registry.
	registerAllStoresOn( registry );

	return registry;
};

/**
 * Wraps children components with a fresh test registry,
 * which can be configured by its callback prop.
 *
 * @since 1.7.1
 * @private
 *
 * @param {?Object}   props          Component props.
 * @param {?Function} props.callback Function which receives the registry instance.
 * @param {?Object}   props.registry Registry object; uses `createTestRegistry()` by default.
 * @return {WPElement} Wrapped components.
 */
export function WithTestRegistry( { children, callback, registry = createTestRegistry() } = {} ) {
	// Populate most basic data which should not affect any tests.
	provideUserInfo( registry );

	if ( callback ) {
		callback( registry );
	}

	return (
		<RegistryProvider value={ registry }>
			{ children }
		</RegistryProvider>
	);
}

/**
 * Provides site connection data to the given registry.
 *
 * By default the site will be set to connected.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteConnection = ( registry, extraData = {} ) => {
	const defaultConnected = extraData.connected !== undefined ? extraData.connected : true;
	const defaults = {
		connected: defaultConnected,
		resettable: defaultConnected,
		setupCompleted: defaultConnected,
		hasConnectedAdmins: defaultConnected,
		ownerID: defaultConnected ? 1 : 0,
	};

	registry.dispatch( CORE_SITE ).receiveGetConnection( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user authentication data to the given registry.
 *
 * By default the user will be set to authenticated.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserAuthentication = ( registry, extraData = {} ) => {
	const defaults = {
		authenticated: true,
		requiredScopes: [],
		grantedScopes: [],
		unsatisfiedScopes: [],
		needsReauthentication: false,
	};

	const mergedData = { ...defaults, ...extraData };
	registry.dispatch( CORE_USER ).receiveGetAuthentication( mergedData );

	// Also set verification info here based on authentication.
	registry.dispatch( CORE_USER ).receiveUserIsVerified( mergedData.authenticated );
};

/**
 * Provides site information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteInfo = ( registry, extraData = {} ) => {
	const defaults = {
		adminURL: 'http://example.com/wp-admin',
		ampMode: false,
		currentEntityID: null,
		currentEntityTitle: null,
		currentEntityType: null,
		currentEntityURL: null,
		homeURL: 'http://example.com',
		proxyPermissionsURL: 'https://sitekit.withgoogle.com/site-management/permissions/',
		proxySetupURL: 'https://sitekit.withgoogle.com/site-management/setup/',
		referenceSiteURL: 'http://example.com',
		siteName: 'My Site Name',
		timezone: 'America/Detroit',
		usingProxy: true,
	};

	registry.dispatch( CORE_SITE ).receiveSiteInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserInfo = ( registry, extraData = {} ) => {
	const defaults = {
		id: 1,
		name: 'Wapuu WordPress',
		email: 'wapuu.wordpress@gmail.com',
		picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png',
	};

	registry.dispatch( CORE_USER ).receiveUserInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides modules data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module objects to be merged with defaults. Default empty array.
 */
export const provideModules = ( registry, extraData = [] ) => {
	const extraModules = extraData.reduce( ( acc, module ) => {
		return { ...acc, [ module.slug ]: module };
	}, {} );

	const moduleSlugs = coreModulesFixture.map( ( { slug } ) => slug );
	const modules = coreModulesFixture
		.map( ( module ) => {
			if ( extraModules[ module.slug ] ) {
				return { ...module, ...extraModules[ module.slug ] };
			}
			return { ...module };
		} )
		.concat(
			extraData.filter( ( { slug } ) => ! moduleSlugs.includes( slug ) ),
		);

	registry.dispatch( CORE_MODULES ).receiveGetModules( modules );
};

/**
 * Provides module registration data to the given registry.
 *
 * @since 1.23.0
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module registration data objects to be merged with defaults. Default empty array.
 */
export const provideModuleRegistrations = ( registry, extraData = [] ) => {
	const extraDataBySlug = extraData.reduce( ( acc, { slug, ...data } ) => {
		return { ...acc, [ slug ]: { slug, ...data } };
	}, {} );
	const { registerModule: realRegisterModule, ...Modules } = coreModules.createModules( registry );
	// Decorate `Modules.registerModule` with a function to apply extra data.
	const registeredModules = {};
	const testRegisterModule = ( ( slug, settings ) => {
		registeredModules[ slug ] = true;
		return realRegisterModule( slug, { settings, ...extraDataBySlug[ slug ] } );
	} );
	Modules.registerModule = testRegisterModule;

	allModules.forEach( ( { registerModule } ) => registerModule?.( Modules ) );
	// Register any additional modules provided.
	Object.entries( extraDataBySlug )
		.filter( ( [ slug ] ) => registeredModules[ slug ] !== true )
		.forEach( ( [ slug, settings ] ) => realRegisterModule( slug, settings ) );
};

/**
 * Mutes a fetch request to the given URL once.
 *
 * Useful for mocking a request for the purpose of preventing a fetch error
 * where the response itself is not significant but the request should not fail.
 * Sometimes a different response may be required to match the expected type,
 * but for anything else, a full mock should be used.
 *
 * @since 1.10.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher    Criteria for deciding which requests to mock.
 *                                                        (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 * @param {*}                                   [response] Optional. Response to return.
 */
export const muteFetch = ( matcher, response = {} ) => {
	fetchMock.once( matcher, { body: response, status: 200 } );
};

/**
 * Mocks a fetch request in a way so that a response is never returned.
 *
 * Useful for simulating a loading state.
 *
 * @since 1.12.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher Criteria for deciding which requests to mock.
 *                                                      (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 */
export const freezeFetch = ( matcher ) => {
	fetchMock.once( matcher, new Promise( () => {
	} ) );
};

/**
 * Registers all Site Kit stores on a registry.
 *
 * Use this to register every available Site Kit store on a registry.
 * Useful for testing, when you want to ensure that every registry is
 * available for connected components and data store tests to use.
 *
 * @since 1.5.0
 * @private
 *
 * @param {wp.data.registry} registry Registry to register each store on.
 */
export const registerAllStoresOn = ( registry ) => {
	[
		...allCoreStores,
		...allModules,
	].forEach( ( { registerStore } ) => registerStore?.( registry ) );
};

const unsubscribes = [];
export const subscribeWithUnsubscribe = ( registry, ...args ) => {
	const unsubscribe = registry.subscribe( ...args );
	unsubscribes.push( unsubscribe );
	return unsubscribe;
};

/**
 * Returns an object that returns hasFinishedResolution selectors for each key
 * that are bound to the given registry and store name.
 *
 * @example
 * await untilResolved( registry, STORE_NAME ).selectorWithResolver( arg1, arg2, arg3 );
 *
 * @since 1.11.0
 * @private
 *
 * @param {Object} registry  WP data registry instance.
 * @param {string} storeName Store name the selector belongs to.
 * @return {Object} Object with keys as functions for each resolver in the given store.
 */
export const untilResolved = ( registry, storeName ) => {
	return mapValues(
		registry.stores[ storeName ].resolvers || {},
		( resolverFn, resolverName ) => ( ...args ) => {
			return subscribeUntil(
				registry,
				() => registry.select( storeName ).hasFinishedResolution( resolverName, args ),
			);
		},
	);
};

export const subscribeUntil = ( registry, predicates ) => {
	predicates = castArray( predicates );

	return new Promise( ( resolve ) => {
		subscribeWithUnsubscribe( registry, () => {
			if ( predicates.every( ( predicate ) => predicate() ) ) {
				resolve();
			}
		} );
	} );
};

export const unsubscribeFromAll = () => {
	let unsubscribe;
	while ( ( unsubscribe = unsubscribes.shift() ) ) {
		unsubscribe();
	}
};

/**
 * Returns a rejection.
 *
 * Used to ensure that a test will fail if it reaches this point.
 * Useful for asynchronous code that has multiple code paths, eg:
 *
 * ```js
 * try {
 *   await codeThatThrowsAnError();
 *   return unexpectedSuccess();
 * } catch (err) {
 *   expect(err.message).toEqual('Some error.');
 * }
 * ```
 *
 * Use this to ensure that the unintended path throws an error rather than
 * silently succeed.
 *
 * @since 1.5.0
 * @private
 *
 * @return {Promise} A rejected promise.
 */
export const unexpectedSuccess = () => {
	return Promise.reject( new Error(
		'Some code (likely a Promise) succeeded unexpectedly; check your test.',
	) );
};
<commit_msg>Fix implementation of wrapped registerModule in provideModuleRegistrations.<commit_after>/**
 * External dependencies
 */
import castArray from 'lodash/castArray';
import mapValues from 'lodash/mapValues';
import fetchMock from 'fetch-mock';

/**
 * WordPress dependencies
 */
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
 * Internal dependencies
 */
import * as coreSite from '../../assets/js/googlesitekit/datastore/site';
import * as coreUser from '../../assets/js/googlesitekit/datastore/user';
import * as coreForms from '../../assets/js/googlesitekit/datastore/forms';
import * as coreLocation from '../../assets/js/googlesitekit/datastore/location';
import * as coreModules from '../../assets/js/googlesitekit/modules';
import * as coreWidgets from '../../assets/js/googlesitekit/widgets';
import * as modulesAdSense from '../../assets/js/modules/adsense';
import * as modulesAnalytics from '../../assets/js/modules/analytics';
import * as modulesPageSpeedInsights from '../../assets/js/modules/pagespeed-insights';
import * as modulesSearchConsole from '../../assets/js/modules/search-console';
import * as modulesTagManager from '../../assets/js/modules/tagmanager';
import * as modulesOptimize from '../../assets/js/modules/optimize';
import { STORE_NAME as CORE_SITE } from '../../assets/js/googlesitekit/datastore/site/constants';
import { STORE_NAME as CORE_USER } from '../../assets/js/googlesitekit/datastore/user/constants';
import { STORE_NAME as CORE_MODULES } from '../../assets/js/googlesitekit/modules/datastore/constants';
import coreModulesFixture from '../../assets/js/googlesitekit/modules/datastore/fixtures.json';

const allCoreStores = [
	coreForms,
	coreLocation,
	coreModules,
	coreSite,
	coreUser,
	coreWidgets,
];
const allModules = [
	modulesAdSense,
	modulesAnalytics,
	modulesOptimize,
	modulesPageSpeedInsights,
	modulesSearchConsole,
	modulesTagManager,
];

/**
 * Creates a registry with all available stores.
 *
 * @since 1.5.0
 * @private
 *
 * @return {wp.data.registry} Registry with all available stores registered.
 */
export const createTestRegistry = () => {
	const registry = createRegistry();

	// Register all available stores on the registry.
	registerAllStoresOn( registry );

	return registry;
};

/**
 * Wraps children components with a fresh test registry,
 * which can be configured by its callback prop.
 *
 * @since 1.7.1
 * @private
 *
 * @param {?Object}   props          Component props.
 * @param {?Function} props.callback Function which receives the registry instance.
 * @param {?Object}   props.registry Registry object; uses `createTestRegistry()` by default.
 * @return {WPElement} Wrapped components.
 */
export function WithTestRegistry( { children, callback, registry = createTestRegistry() } = {} ) {
	// Populate most basic data which should not affect any tests.
	provideUserInfo( registry );

	if ( callback ) {
		callback( registry );
	}

	return (
		<RegistryProvider value={ registry }>
			{ children }
		</RegistryProvider>
	);
}

/**
 * Provides site connection data to the given registry.
 *
 * By default the site will be set to connected.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteConnection = ( registry, extraData = {} ) => {
	const defaultConnected = extraData.connected !== undefined ? extraData.connected : true;
	const defaults = {
		connected: defaultConnected,
		resettable: defaultConnected,
		setupCompleted: defaultConnected,
		hasConnectedAdmins: defaultConnected,
		ownerID: defaultConnected ? 1 : 0,
	};

	registry.dispatch( CORE_SITE ).receiveGetConnection( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user authentication data to the given registry.
 *
 * By default the user will be set to authenticated.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserAuthentication = ( registry, extraData = {} ) => {
	const defaults = {
		authenticated: true,
		requiredScopes: [],
		grantedScopes: [],
		unsatisfiedScopes: [],
		needsReauthentication: false,
	};

	const mergedData = { ...defaults, ...extraData };
	registry.dispatch( CORE_USER ).receiveGetAuthentication( mergedData );

	// Also set verification info here based on authentication.
	registry.dispatch( CORE_USER ).receiveUserIsVerified( mergedData.authenticated );
};

/**
 * Provides site information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteInfo = ( registry, extraData = {} ) => {
	const defaults = {
		adminURL: 'http://example.com/wp-admin',
		ampMode: false,
		currentEntityID: null,
		currentEntityTitle: null,
		currentEntityType: null,
		currentEntityURL: null,
		homeURL: 'http://example.com',
		proxyPermissionsURL: 'https://sitekit.withgoogle.com/site-management/permissions/',
		proxySetupURL: 'https://sitekit.withgoogle.com/site-management/setup/',
		referenceSiteURL: 'http://example.com',
		siteName: 'My Site Name',
		timezone: 'America/Detroit',
		usingProxy: true,
	};

	registry.dispatch( CORE_SITE ).receiveSiteInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserInfo = ( registry, extraData = {} ) => {
	const defaults = {
		id: 1,
		name: 'Wapuu WordPress',
		email: 'wapuu.wordpress@gmail.com',
		picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png',
	};

	registry.dispatch( CORE_USER ).receiveUserInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides modules data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module objects to be merged with defaults. Default empty array.
 */
export const provideModules = ( registry, extraData = [] ) => {
	const extraModules = extraData.reduce( ( acc, module ) => {
		return { ...acc, [ module.slug ]: module };
	}, {} );

	const moduleSlugs = coreModulesFixture.map( ( { slug } ) => slug );
	const modules = coreModulesFixture
		.map( ( module ) => {
			if ( extraModules[ module.slug ] ) {
				return { ...module, ...extraModules[ module.slug ] };
			}
			return { ...module };
		} )
		.concat(
			extraData.filter( ( { slug } ) => ! moduleSlugs.includes( slug ) ),
		);

	registry.dispatch( CORE_MODULES ).receiveGetModules( modules );
};

/**
 * Provides module registration data to the given registry.
 *
 * @since 1.23.0
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module registration data objects to be merged with defaults. Default empty array.
 */
export const provideModuleRegistrations = ( registry, extraData = [] ) => {
	const extraDataBySlug = extraData.reduce( ( acc, { slug, ...data } ) => {
		return { ...acc, [ slug ]: { slug, ...data } };
	}, {} );
	const { registerModule: realRegisterModule, ...Modules } = coreModules.createModules( registry );
	// Decorate `Modules.registerModule` with a function to apply extra data.
	const registeredModules = {};
	const testRegisterModule = ( ( slug, settings ) => {
		registeredModules[ slug ] = true;
		return realRegisterModule( slug, { ...settings, ...extraDataBySlug[ slug ] } );
	} );
	Modules.registerModule = testRegisterModule;

	allModules.forEach( ( { registerModule } ) => registerModule?.( Modules ) );
	// Register any additional modules provided.
	Object.entries( extraDataBySlug )
		.filter( ( [ slug ] ) => registeredModules[ slug ] !== true )
		.forEach( ( [ slug, settings ] ) => realRegisterModule( slug, settings ) );
};

/**
 * Mutes a fetch request to the given URL once.
 *
 * Useful for mocking a request for the purpose of preventing a fetch error
 * where the response itself is not significant but the request should not fail.
 * Sometimes a different response may be required to match the expected type,
 * but for anything else, a full mock should be used.
 *
 * @since 1.10.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher    Criteria for deciding which requests to mock.
 *                                                        (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 * @param {*}                                   [response] Optional. Response to return.
 */
export const muteFetch = ( matcher, response = {} ) => {
	fetchMock.once( matcher, { body: response, status: 200 } );
};

/**
 * Mocks a fetch request in a way so that a response is never returned.
 *
 * Useful for simulating a loading state.
 *
 * @since 1.12.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher Criteria for deciding which requests to mock.
 *                                                      (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 */
export const freezeFetch = ( matcher ) => {
	fetchMock.once( matcher, new Promise( () => {
	} ) );
};

/**
 * Registers all Site Kit stores on a registry.
 *
 * Use this to register every available Site Kit store on a registry.
 * Useful for testing, when you want to ensure that every registry is
 * available for connected components and data store tests to use.
 *
 * @since 1.5.0
 * @private
 *
 * @param {wp.data.registry} registry Registry to register each store on.
 */
export const registerAllStoresOn = ( registry ) => {
	[
		...allCoreStores,
		...allModules,
	].forEach( ( { registerStore } ) => registerStore?.( registry ) );
};

const unsubscribes = [];
export const subscribeWithUnsubscribe = ( registry, ...args ) => {
	const unsubscribe = registry.subscribe( ...args );
	unsubscribes.push( unsubscribe );
	return unsubscribe;
};

/**
 * Returns an object that returns hasFinishedResolution selectors for each key
 * that are bound to the given registry and store name.
 *
 * @example
 * await untilResolved( registry, STORE_NAME ).selectorWithResolver( arg1, arg2, arg3 );
 *
 * @since 1.11.0
 * @private
 *
 * @param {Object} registry  WP data registry instance.
 * @param {string} storeName Store name the selector belongs to.
 * @return {Object} Object with keys as functions for each resolver in the given store.
 */
export const untilResolved = ( registry, storeName ) => {
	return mapValues(
		registry.stores[ storeName ].resolvers || {},
		( resolverFn, resolverName ) => ( ...args ) => {
			return subscribeUntil(
				registry,
				() => registry.select( storeName ).hasFinishedResolution( resolverName, args ),
			);
		},
	);
};

export const subscribeUntil = ( registry, predicates ) => {
	predicates = castArray( predicates );

	return new Promise( ( resolve ) => {
		subscribeWithUnsubscribe( registry, () => {
			if ( predicates.every( ( predicate ) => predicate() ) ) {
				resolve();
			}
		} );
	} );
};

export const unsubscribeFromAll = () => {
	let unsubscribe;
	while ( ( unsubscribe = unsubscribes.shift() ) ) {
		unsubscribe();
	}
};

/**
 * Returns a rejection.
 *
 * Used to ensure that a test will fail if it reaches this point.
 * Useful for asynchronous code that has multiple code paths, eg:
 *
 * ```js
 * try {
 *   await codeThatThrowsAnError();
 *   return unexpectedSuccess();
 * } catch (err) {
 *   expect(err.message).toEqual('Some error.');
 * }
 * ```
 *
 * Use this to ensure that the unintended path throws an error rather than
 * silently succeed.
 *
 * @since 1.5.0
 * @private
 *
 * @return {Promise} A rejected promise.
 */
export const unexpectedSuccess = () => {
	return Promise.reject( new Error(
		'Some code (likely a Promise) succeeded unexpectedly; check your test.',
	) );
};
<|endoftext|>


<commit_before>/**
 * External dependencies
 */
import { castArray } from 'lodash';

/**
 * WordPress dependencies
 */
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
 * Internal dependencies
 */
import coreSiteStore, { STORE_NAME as coreSiteStoreName } from '../../assets/js/googlesitekit/datastore/site';
import coreUserStore, { STORE_NAME as coreUserStoreName } from '../../assets/js/googlesitekit/datastore/user';
import coreModulesStore, { STORE_NAME as coreModulesStoreName } from '../../assets/js/googlesitekit/modules/datastore';
import modulesAdSenseStore, { STORE_NAME as modulesAdSenseStoreName } from '../../assets/js/modules/adsense/datastore';
import modulesAnalyticsStore, { STORE_NAME as modulesAnalyticsStoreName } from '../../assets/js/modules/analytics/datastore';

/**
 * Create a registry with all available stores.
 *
 * @since 1.5.0
 *
 * @return {wp.data.registry} Registry with all available stores registered.
 */
export const createTestRegistry = () => {
	const registry = createRegistry();

	// Register all available stores on the registry.
	registerAllStoresOn( registry );

	// Populate most basic data which should not affect any tests.
	registry.dispatch( coreUserStoreName ).receiveUserInfo( {
		id: 1,
		name: 'Wapuu WordPress',
		email: 'wapuu.wordpress@gmail.com',
		picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png',
	} );

	return registry;
};

/**
 * Wraps children components with a fresh test registry,
 * which can be configured by its callback prop.
 *
 * @since 1.7.1
 *
 * @param {?Object}   props          Component props.
 * @param {?Function} props.callback Function which receives the registry instance.
 * @param {?Object}   props.registry Registry object; uses `createTestRegistry()` by default.
 * @return {WPElement} Wrapped components.
 */
export function WithTestRegistry( { children, callback, registry = createTestRegistry() } = {} ) {
	if ( callback ) {
		callback( registry );
	}

	return (
		<RegistryProvider value={ registry }>
			{ children }
		</RegistryProvider>
	);
}

/**
 * Mute a given console during tests.
 *
 * Use this to mute expect console output during tests for things like
 * API fetch errors or other things you expect to log to console but don't
 * want appearing in the jest output.
 *
 * @since 1.5.0
 *
 * @param {string} type  Type of console to mute (one of: `'error'`, `'warn'`, `'log'`, `'info'`, or `'debug'`)
 * @param {number} times Number of times to mute console output perform resuming.
 */
export const muteConsole = ( type = 'error', times = 1 ) => {
	Array.from( { length: times } ).forEach( () => {
		global.console[ type ].mockImplementationOnce( () => jest.fn() );
	} );
};

/**
 * Register all Site Kit stores on a registry.
 *
 * Use this to register every available Site Kit store on a registry.
 * Useful for testing, when you want to ensure that every registry is
 * available for connected components and data store tests to use.
 *
 * @since 1.5.0
 *
 * @param {wp.data.registry} registry Registry to register each store on.
 */
export const registerAllStoresOn = ( registry ) => {
	registry.registerStore( coreSiteStoreName, coreSiteStore );
	registry.registerStore( coreUserStoreName, coreUserStore );
	registry.registerStore( coreModulesStoreName, coreModulesStore );
	registry.registerStore( modulesAdSenseStoreName, modulesAdSenseStore );
	registry.registerStore( modulesAnalyticsStoreName, modulesAnalyticsStore );
};

const unsubscribes = [];
export const subscribeWithUnsubscribe = ( registry, ...args ) => {
	const unsubscribe = registry.subscribe( ...args );
	unsubscribes.push( unsubscribe );
	return unsubscribe;
};

export const subscribeUntil = ( registry, predicates ) => {
	predicates = castArray( predicates );

	return new Promise( ( resolve ) => {
		subscribeWithUnsubscribe( registry, () => {
			if ( predicates.every( ( predicate ) => predicate() ) ) {
				resolve();
			}
		} );
	} );
};

export const unsubscribeFromAll = () => {
	let unsubscribe;
	while ( ( unsubscribe = unsubscribes.shift() ) ) {
		unsubscribe();
	}
};

/**
 * Return a rejection.
 *
 * Used to ensure that a test will fail if it reaches this point.
 * Useful for asynchronous code that has multiple code paths, eg:
 *
 * ```js
 * try {
 *   await codeThatThrowsAnError();
 *   return unexpectedSuccess();
 * } catch (err) {
 *   expect(err.message).toEqual('Some error.');
 * }
 * ```
 *
 * Use this to ensure that the unintended path throws an error rather than
 * silently succeed.
 *
 * @since 1.5.0
 *
 * @return {Promise} A rejected promise.
 */
export const unexpectedSuccess = () => {
	return Promise.reject( new Error(
		'Some code (likely a Promise) succeeded unexpectedly; check your test.'
	) );
};
<commit_msg>Move basic test registry setup to WithTestRegistry since it should only be applicable to e2e and storybook, but not Jest which are lower-level.<commit_after>/**
 * External dependencies
 */
import { castArray } from 'lodash';

/**
 * WordPress dependencies
 */
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
 * Internal dependencies
 */
import coreSiteStore, { STORE_NAME as coreSiteStoreName } from '../../assets/js/googlesitekit/datastore/site';
import coreUserStore, { STORE_NAME as coreUserStoreName } from '../../assets/js/googlesitekit/datastore/user';
import coreModulesStore, { STORE_NAME as coreModulesStoreName } from '../../assets/js/googlesitekit/modules/datastore';
import modulesAdSenseStore, { STORE_NAME as modulesAdSenseStoreName } from '../../assets/js/modules/adsense/datastore';
import modulesAnalyticsStore, { STORE_NAME as modulesAnalyticsStoreName } from '../../assets/js/modules/analytics/datastore';

/**
 * Create a registry with all available stores.
 *
 * @since 1.5.0
 *
 * @return {wp.data.registry} Registry with all available stores registered.
 */
export const createTestRegistry = () => {
	const registry = createRegistry();

	// Register all available stores on the registry.
	registerAllStoresOn( registry );

	return registry;
};

/**
 * Wraps children components with a fresh test registry,
 * which can be configured by its callback prop.
 *
 * @since 1.7.1
 *
 * @param {?Object}   props          Component props.
 * @param {?Function} props.callback Function which receives the registry instance.
 * @param {?Object}   props.registry Registry object; uses `createTestRegistry()` by default.
 * @return {WPElement} Wrapped components.
 */
export function WithTestRegistry( { children, callback, registry = createTestRegistry() } = {} ) {
	// Populate most basic data which should not affect any tests.
	registry.dispatch( coreUserStoreName ).receiveUserInfo( {
		id: 1,
		name: 'Wapuu WordPress',
		email: 'wapuu.wordpress@gmail.com',
		picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png',
	} );

	if ( callback ) {
		callback( registry );
	}

	return (
		<RegistryProvider value={ registry }>
			{ children }
		</RegistryProvider>
	);
}

/**
 * Mute a given console during tests.
 *
 * Use this to mute expect console output during tests for things like
 * API fetch errors or other things you expect to log to console but don't
 * want appearing in the jest output.
 *
 * @since 1.5.0
 *
 * @param {string} type  Type of console to mute (one of: `'error'`, `'warn'`, `'log'`, `'info'`, or `'debug'`)
 * @param {number} times Number of times to mute console output perform resuming.
 */
export const muteConsole = ( type = 'error', times = 1 ) => {
	Array.from( { length: times } ).forEach( () => {
		global.console[ type ].mockImplementationOnce( () => jest.fn() );
	} );
};

/**
 * Register all Site Kit stores on a registry.
 *
 * Use this to register every available Site Kit store on a registry.
 * Useful for testing, when you want to ensure that every registry is
 * available for connected components and data store tests to use.
 *
 * @since 1.5.0
 *
 * @param {wp.data.registry} registry Registry to register each store on.
 */
export const registerAllStoresOn = ( registry ) => {
	registry.registerStore( coreSiteStoreName, coreSiteStore );
	registry.registerStore( coreUserStoreName, coreUserStore );
	registry.registerStore( coreModulesStoreName, coreModulesStore );
	registry.registerStore( modulesAdSenseStoreName, modulesAdSenseStore );
	registry.registerStore( modulesAnalyticsStoreName, modulesAnalyticsStore );
};

const unsubscribes = [];
export const subscribeWithUnsubscribe = ( registry, ...args ) => {
	const unsubscribe = registry.subscribe( ...args );
	unsubscribes.push( unsubscribe );
	return unsubscribe;
};

export const subscribeUntil = ( registry, predicates ) => {
	predicates = castArray( predicates );

	return new Promise( ( resolve ) => {
		subscribeWithUnsubscribe( registry, () => {
			if ( predicates.every( ( predicate ) => predicate() ) ) {
				resolve();
			}
		} );
	} );
};

export const unsubscribeFromAll = () => {
	let unsubscribe;
	while ( ( unsubscribe = unsubscribes.shift() ) ) {
		unsubscribe();
	}
};

/**
 * Return a rejection.
 *
 * Used to ensure that a test will fail if it reaches this point.
 * Useful for asynchronous code that has multiple code paths, eg:
 *
 * ```js
 * try {
 *   await codeThatThrowsAnError();
 *   return unexpectedSuccess();
 * } catch (err) {
 *   expect(err.message).toEqual('Some error.');
 * }
 * ```
 *
 * Use this to ensure that the unintended path throws an error rather than
 * silently succeed.
 *
 * @since 1.5.0
 *
 * @return {Promise} A rejected promise.
 */
export const unexpectedSuccess = () => {
	return Promise.reject( new Error(
		'Some code (likely a Promise) succeeded unexpectedly; check your test.'
	) );
};
<|endoftext|>


<commit_before>describe("Navy.Class is class base OOP:", function() {
  /**
   * @class ClassA
   */
  Navy.Class('ClassA', {
    arg1: null,
    arg2: null,
    obj: null,

    initialize: function(arg1, arg2) {
      this.arg1 = arg1;
      this.arg2 = arg2;

      this.obj = {};
    },

    say: function() {
      return 'ClassA#say';
    }
  });

  /**
   * @class ClassB
   */
  Navy.Class('ClassB', {
    say: function() {
      return 'ClassB#sayB';
    }
  });

  it("create instance.", function() {
    var a = new ClassA();
    var b = new ClassB();

    expect(a instanceof ClassA).toBeTruthy();
    expect(b instanceof ClassB).toBeTruthy();
  });

  it('initialize function is called when use "new"', function() {
    var arg1 = 100;
    var arg2 = [-1, -2];
    var a = new ClassA(arg1, arg2);

    expect(a.arg1).toBe(arg1);
    expect(a.arg2).toBe(arg2);
  });

  it('can call method', function() {
    var a = new ClassA();
    expect(a.say()).toBe('ClassA#say');
  });

  it('each instance property are independence.', function() {
    var a1 = new ClassA();
    var a2 = new ClassA();

    a1.obj['food'] = 'rice';

    expect(a1.obj['food']).toBe('rice');
    expect(a2.obj['food']).toBeUndefined();
    expect(a1.obj).not.toBe(a2.obj);
  });
});

describe('Navy.Class special property', function(){
  /**
   * @class Foo.Bar.ClassA
   */
  Navy.Class('Foo.Bar.ClassA', {});

  it('can access constructor', function() {
    var a = new Foo.Bar.ClassA();

    expect(a.$class).toBe(Foo.Bar.ClassA);
  });

  it('can access class name', function() {
    var a = new Foo.Bar.ClassA();

    expect(a.$className).toBe('Foo.Bar.ClassA');
  });

  it('can access constructor function name', function() {
    expect(Foo.Bar.ClassA.name).toBe('Foo$Bar$ClassA');
  });
});

describe('Navy.Class static property and method', function() {
  /**
   * @class ClassC
   */
  Navy.Class('ClassC', {
    $static: {
      value: {x: 10},

      getValue: function(){
        return this.value;
      },

      setValue: function(x) {
        this.value.x = x;
      }
    }
  });

  it('has static property', function() {
    var a1 = new ClassC();
    var a2 = new ClassC();

    expect(a1.$class.x).toBe(ClassC.x);
    expect(a2.$class.x).toBe(ClassC.x);
  });

  it('call static method', function() {
    var a1 = new ClassC();
    var a2 = new ClassC();

    ClassC.setValue(100);
    expect(a1.$class.getValue()).toBe(ClassC.getValue());
    expect(a2.$class.getValue()).toBe(ClassC.getValue());
  });
});

describe('Navy.Class exception', function() {
  it('throw property error when property is Object', function() {
    try {
      Navy.Class('ClassA', {
        obj: {}
      });
    } catch(e) {
      expect(true).toBeTruthy();
      return;
    }

    expect(true).toBeFalsy();
  });

  it('throw argument error when argument number is not enough', function() {
    try {
      Navy.Class();
    } catch(e) {
      expect(true).toBeTruthy();
    }

    try {
      Navy.Class('ClassA');
    } catch(e) {
      expect(true).toBeTruthy();
      return;
    }

    expect(true).toBeFalsy();
  });

  it('throws not function exception.', function(){
    var exception = null;
    try {
      Navy.Class._wrapFunction({doSomething: 'not function'}, 'doSomething', function(){});
    } catch(e) {
      exception = e;
    }

    expect(exception).not.toBeNull();
  });

  it('throws already defined exception.', function(){
    var exception = null;

    Navy.Class('ClassA5', {});
    try {
      Navy.Class('ClassA5', {});
    } catch(e) {
      exception = e;
    }

    expect(exception).not.toBeNull();
  });
});

describe('Navy.Class extend', function() {
  /**
   * @class classAA
   */
  Navy.Class('ClassAA', {
    x: 0,
    initialize: function(arg) {
      this.x = arg;
    },

    getX: function() {
      return this.x;
    },

    say: function() {
      return 'ClassA';
    }
  });

  /**
   * @class classBB
   */
  Navy.Class('ClassBB', ClassAA, {
    initialize: function($super, arg) {
      $super(arg);
    },

    say: function($super) {
      var result = $super();
      return result + ':ClassB';
    }
  });

  it('extend class', function() {
    var b = new ClassBB();

    expect(b instanceof ClassBB).toBeTruthy();
    expect(b instanceof ClassAA).toBeTruthy();
  });

  it('can override initialize method', function() {
    var value = {num: 10};
    var b = new ClassBB(value);

    expect(b.x).toBe(value);
  });

  it('can extend method', function() {
    var value = {num: 10};
    var b = new ClassBB(value);

    expect(b.getX()).toBe(value);
  });

  it('can override method', function() {
    var b = new ClassBB();

    expect(b.say()).toBe('ClassA:ClassB');
  });
});

describe('Navy.Class extend static', function() {
  /**
   * @class ClassAAA
   */
  Navy.Class('ClassAAA', {
    $static: {
      say: function() {
        return 'ClassA';
      }
    }
  });

  Navy.Class('ClassBBB', ClassAAA, {
    $static: {
      say: function($super) {
        return $super() + ':ClassB';
      }
    }
  });

  it('can override method', function() {
    expect(ClassBBB.say()).toBe('ClassA:ClassB');
  });
});

describe('Navy.Class.instance create singleton instance:', function(){
  /**
   * @typedef {Object} Instance1
   */
  Navy.Class.instance('Instance1', {
    getName: function() {
      return 'Instance1';
    }
  });

  /**
   * @class Class4
   */
  Navy.Class('Class4', Instance1, {
    getName: function($super) {
      return $super() + ':Class4';
    }
  });

  it('can be used like singleton.', function(){
    expect(Instance1.getName()).toBe('Instance1');
  });

  it('can extend singleton instance', function(){
    var obj = new Class4();
    expect(obj.getName()).toBe('Instance1:Class4');
  });
});
<commit_msg>クラスのデフォルトプロパティにオブジェクトが指定された場合のspec追加.<commit_after>describe("Navy.Class is class base OOP:", function() {
  /**
   * @class ClassA
   */
  Navy.Class('ClassA', {
    arg1: null,
    arg2: null,
    obj: null,

    initialize: function(arg1, arg2) {
      this.arg1 = arg1;
      this.arg2 = arg2;

      this.obj = {};
    },

    say: function() {
      return 'ClassA#say';
    }
  });

  /**
   * @class ClassB
   */
  Navy.Class('ClassB', {
    say: function() {
      return 'ClassB#sayB';
    }
  });

  it("create instance.", function() {
    var a = new ClassA();
    var b = new ClassB();

    expect(a instanceof ClassA).toBeTruthy();
    expect(b instanceof ClassB).toBeTruthy();
  });

  it('initialize function is called when use "new"', function() {
    var arg1 = 100;
    var arg2 = [-1, -2];
    var a = new ClassA(arg1, arg2);

    expect(a.arg1).toBe(arg1);
    expect(a.arg2).toBe(arg2);
  });

  it('can call method', function() {
    var a = new ClassA();
    expect(a.say()).toBe('ClassA#say');
  });

  it('each instance property are independence.', function() {
    var a1 = new ClassA();
    var a2 = new ClassA();

    a1.obj['food'] = 'rice';

    expect(a1.obj['food']).toBe('rice');
    expect(a2.obj['food']).toBeUndefined();
    expect(a1.obj).not.toBe(a2.obj);
  });
});

describe('Navy.Class special property', function(){
  /**
   * @class Foo.Bar.ClassA
   */
  Navy.Class('Foo.Bar.ClassA', {});

  it('can access constructor', function() {
    var a = new Foo.Bar.ClassA();

    expect(a.$class).toBe(Foo.Bar.ClassA);
  });

  it('can access class name', function() {
    var a = new Foo.Bar.ClassA();

    expect(a.$className).toBe('Foo.Bar.ClassA');
  });

  it('can access constructor function name', function() {
    expect(Foo.Bar.ClassA.name).toBe('Foo$Bar$ClassA');
  });
});

describe('Navy.Class static property and method', function() {
  /**
   * @class ClassC
   */
  Navy.Class('ClassC', {
    $static: {
      value: {x: 10},

      getValue: function(){
        return this.value;
      },

      setValue: function(x) {
        this.value.x = x;
      }
    }
  });

  it('has static property', function() {
    var a1 = new ClassC();
    var a2 = new ClassC();

    expect(a1.$class.x).toBe(ClassC.x);
    expect(a2.$class.x).toBe(ClassC.x);
  });

  it('call static method', function() {
    var a1 = new ClassC();
    var a2 = new ClassC();

    ClassC.setValue(100);
    expect(a1.$class.getValue()).toBe(ClassC.getValue());
    expect(a2.$class.getValue()).toBe(ClassC.getValue());
  });
});

describe('Navy.Class exception', function() {
  it('throw property error when property is Object', function() {
    try {
      Navy.Class('ClassA', {
        obj: {}
      });
    } catch(e) {
      expect(true).toBeTruthy();
      return;
    }

    expect(true).toBeFalsy();
  });

  it('throw argument error when argument number is not enough', function() {
    try {
      Navy.Class();
    } catch(e) {
      expect(true).toBeTruthy();
    }

    try {
      Navy.Class('ClassA');
    } catch(e) {
      expect(true).toBeTruthy();
      return;
    }

    expect(true).toBeFalsy();
  });

  it('throws not function exception.', function(){
    var exception = null;
    try {
      Navy.Class._wrapFunction({doSomething: 'not function'}, 'doSomething', function(){});
    } catch(e) {
      exception = e;
    }

    expect(exception).not.toBeNull();
  });

  it('throws already defined exception.', function(){
    var exception = null;

    Navy.Class('ClassA5', {});
    try {
      Navy.Class('ClassA5', {});
    } catch(e) {
      exception = e;
    }

    expect(exception).not.toBeNull();
  });

  it('throws object property exception.', function(){
    var exception = null;

    try {
      Navy.Class('InvalidClassA6', {
        prop: {}
      });
    } catch(e) {
      exception = e;
    }

    expect(exception).not.toBeNull();
  });
});

describe('Navy.Class extend', function() {
  /**
   * @class classAA
   */
  Navy.Class('ClassAA', {
    x: 0,
    initialize: function(arg) {
      this.x = arg;
    },

    getX: function() {
      return this.x;
    },

    say: function() {
      return 'ClassA';
    }
  });

  /**
   * @class classBB
   */
  Navy.Class('ClassBB', ClassAA, {
    initialize: function($super, arg) {
      $super(arg);
    },

    say: function($super) {
      var result = $super();
      return result + ':ClassB';
    }
  });

  it('extend class', function() {
    var b = new ClassBB();

    expect(b instanceof ClassBB).toBeTruthy();
    expect(b instanceof ClassAA).toBeTruthy();
  });

  it('can override initialize method', function() {
    var value = {num: 10};
    var b = new ClassBB(value);

    expect(b.x).toBe(value);
  });

  it('can extend method', function() {
    var value = {num: 10};
    var b = new ClassBB(value);

    expect(b.getX()).toBe(value);
  });

  it('can override method', function() {
    var b = new ClassBB();

    expect(b.say()).toBe('ClassA:ClassB');
  });
});

describe('Navy.Class extend static', function() {
  /**
   * @class ClassAAA
   */
  Navy.Class('ClassAAA', {
    $static: {
      say: function() {
        return 'ClassA';
      }
    }
  });

  Navy.Class('ClassBBB', ClassAAA, {
    $static: {
      say: function($super) {
        return $super() + ':ClassB';
      }
    }
  });

  it('can override method', function() {
    expect(ClassBBB.say()).toBe('ClassA:ClassB');
  });
});

describe('Navy.Class.instance create singleton instance:', function(){
  /**
   * @typedef {Object} Instance1
   */
  Navy.Class.instance('Instance1', {
    getName: function() {
      return 'Instance1';
    }
  });

  /**
   * @class Class4
   */
  Navy.Class('Class4', Instance1, {
    getName: function($super) {
      return $super() + ':Class4';
    }
  });

  it('can be used like singleton.', function(){
    expect(Instance1.getName()).toBe('Instance1');
  });

  it('can extend singleton instance', function(){
    var obj = new Class4();
    expect(obj.getName()).toBe('Instance1:Class4');
  });
});
<|endoftext|>