Skip to content

Commit

Permalink
feat: Create experience controls module tests (#1303)
Browse files Browse the repository at this point in the history
  • Loading branch information
annacv authored Oct 10, 2023
1 parent 5868a24 commit 98acc0f
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ExperienceControlsResponse } from '@empathyco/x-types';

/**
* Creates a an experience controls response stub.
*
* @returns An experience controls stub.
*
* @internal
*/
export function getExperienceControlsStub(): ExperienceControlsResponse {
return createExperienceControlsStub();
}

/**
* Creates an experience controls response.
*
* @returns An experience controls response.
*/
export function createExperienceControlsStub(): ExperienceControlsResponse {
return {
controls: { numberOfCarousels: 10, resultsPerCarousels: 21 },
events: { ColumnsNumberProvided: 6 }
};
}
28 changes: 27 additions & 1 deletion packages/x-components/src/__tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function mergeStates<State extends Dictionary>(
}

/**
* Makes a clean install of the's the {@link XPlugin} into the passed Vue object.
* Makes a clean install of the {@link XPlugin} into the passed Vue object.
* This also resets the bus, and all the hardcoded dependencies of the XPlugin.
*
* @param options - The options for installing the {@link XPlugin}. The
Expand Down Expand Up @@ -221,3 +221,29 @@ export function createXModule<
): XModule<XStoreModule<State, Getters, Mutations, Actions>> {
return xModule;
}

/**
* Mocks a `fetch` API call.
*
* @param response - The expected response resolved by calling `fetch()`.
* @returns A Promise object.
*
* @internal
*/
export function getFetchMock(
response: unknown
): (url: string, params: RequestInit) => Promise<Response> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return _url => {
return new Promise<Response>(resolve => {
setTimeout(() => {
resolve({
ok: true,
status: 200,
json: () => Promise.resolve(response),
text: () => Promise.resolve(JSON.stringify(response))
} as Response);
});
});
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { mount, Wrapper } from '@vue/test-utils';
import Vue from 'vue';
import { XPlugin } from '../../../../plugins/index';
import { installNewXPlugin } from '../../../../__tests__/utils';
import { experienceControlsXModule } from '../../x-module';
import { getXComponentXModuleName, isXComponent } from '../../../../components/index';
import ExperienceControls from '../experience-controls.vue';

function renderExperienceControls(): RenderExperienceControlsApi {
const [, localVue] = installNewXPlugin();
XPlugin.registerXModule(experienceControlsXModule);

const wrapper = mount(ExperienceControls, {
localVue
});

return {
wrapper
};
}

describe('testing experience controls component', () => {
it('is an XComponent which has an XModule', () => {
const { wrapper } = renderExperienceControls();
expect(isXComponent(wrapper.vm)).toEqual(true);
expect(getXComponentXModuleName(wrapper.vm)).toEqual('experienceControls');
});

// eslint-disable-next-line max-len
it('listens to the event ExperienceControlsEventsChanged and emits the events on the payload', () => {
const { wrapper } = renderExperienceControls();

const eventsFromExperienceControls = {
ExtraParamsProvided: {
warehouse: 'Magrathea'
},
SortChanged: 'price:desc'
};

const extraParamsProvidedListener = jest.fn();
wrapper.vm.$x.on('ExtraParamsProvided').subscribe(extraParamsProvidedListener);

const sortChangedListener = jest.fn();
wrapper.vm.$x.on('SortChanged').subscribe(sortChangedListener);

wrapper.vm.$x.emit('ExperienceControlsEventsChanged', eventsFromExperienceControls);

expect(extraParamsProvidedListener).toHaveBeenCalledTimes(1);
expect(extraParamsProvidedListener).toHaveBeenCalledWith({
warehouse: 'Magrathea'
});

expect(sortChangedListener).toHaveBeenCalledTimes(1);
expect(sortChangedListener).toHaveBeenCalledWith('price:desc');
});
});

interface RenderExperienceControlsApi {
/** The wrapper for the experience controls component. */
wrapper: Wrapper<Vue>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import Vuex from 'vuex';
import { createLocalVue } from '@vue/test-utils';
import { getMockedAdapter, installNewXPlugin } from '../../../../__tests__/utils';
import { getExperienceControlsStub } from '../../../../__stubs__/experience-controls-stubs.factory';
import { createExperienceControlsStore, resetExperienceControlsStateWith } from './utils';

describe('testing experience controls module actions', () => {
const mockedResponse = getExperienceControlsStub();

const adapter = getMockedAdapter({
experienceControls: mockedResponse
});

const localVue = createLocalVue();
localVue.config.productionTip = false; // Silent production console messages.
localVue.use(Vuex);
const store = createExperienceControlsStore();
installNewXPlugin({ adapter, store }, localVue);

beforeEach(() => {
resetExperienceControlsStateWith(store);
});

describe('fetchControls', () => {
it('should return experience controls response', async () => {
const experienceControls = await store.dispatch(
'fetchExperienceControlsResponse',
store.getters.experienceControlsRequest
);
expect(experienceControls).toEqual(mockedResponse);
});
});

describe('fetchAndSaveControls', () => {
it('should request and store controls and events in the state', async () => {
const actionPromise = store.dispatch(
'fetchAndSaveExperienceControlsResponse',
store.getters.experienceControlsRequest
);
expect(store.state.status).toEqual('loading');
await actionPromise;

expect(store.state.controls).toEqual(mockedResponse.controls);
expect(store.state.events).toEqual(mockedResponse.events);
expect(store.state.status).toEqual('success');
});

it('should cancel the previous request if it is not yet resolved', async () => {
const initialExperienceControls = store.state.controls;
adapter.experienceControls.mockResolvedValueOnce(mockedResponse);

const firstRequest = store.dispatch(
'fetchAndSaveExperienceControlsResponse',
store.getters.experienceControlsRequest
);
const secondRequest = store.dispatch(
'fetchAndSaveExperienceControlsResponse',
store.getters.experienceControlsRequest
);

await firstRequest;
expect(store.state.status).toEqual('loading');
expect(store.state.controls).toBe(initialExperienceControls);
await secondRequest;
expect(store.state.status).toEqual('success');
expect(store.state.controls).toEqual(mockedResponse.controls);
});
});

describe('cancelFetchAndSaveControls', () => {
it('should cancel the request and do not modify the stored controls', async () => {
resetExperienceControlsStateWith(store, {
controls: { numberOfCarousels: 20, resultsPerCarousels: 6 }
});
const previousControls = store.state.controls;
await Promise.all([
store.dispatch(
'fetchAndSaveExperienceControlsResponse',
store.getters.experienceControlsRequest
),
store.dispatch('cancelFetchAndSaveControls')
]);
expect(store.state.controls).toEqual(previousControls);
expect(store.state.status).toEqual('success');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Vuex from 'vuex';
import { ExperienceControlsRequest } from '@empathyco/x-types';
import { createLocalVue } from '@vue/test-utils';
import { createExperienceControlsStore, resetExperienceControlsStateWith } from './utils';

describe('testing experience controls module getters', () => {
const localVue = createLocalVue();
localVue.config.productionTip = false;
localVue.use(Vuex);
const store = createExperienceControlsStore();

beforeEach(() => {
resetExperienceControlsStateWith(store);
});

describe(`request getter`, () => {
it('should return a request object', () => {
resetExperienceControlsStateWith(store, {
params: {
store: 'es'
}
});

expect(store.getters.experienceControlsRequest).toEqual<ExperienceControlsRequest>({
extraParams: {
store: 'es'
}
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { DeepPartial } from '@empathyco/x-utils';
import { Store } from 'vuex';
import { resetStoreModuleState } from '../../../../__tests__/utils';
import { experienceControlsXStoreModule } from '../module';
import {
ExperienceControlsActions,
ExperienceControlsGetters,
ExperienceControlsMutations,
ExperienceControlsState
} from '../types';
import { SafeStore } from '../../../../store/__tests__/utils';

/**
* Resets the experience controls module state, optionally modifying its default values.
*
* @param store - Experience controls store state.
* @param state - Partial experience controls store state to replace the original one.
*
* @internal
*/
export function resetExperienceControlsStateWith(
store: Store<ExperienceControlsState>,
state?: DeepPartial<ExperienceControlsState>
): void {
resetStoreModuleState(store, experienceControlsXStoreModule.state(), state);
}

/**
* Creates an experience controls store with the state passed as parameter.
*
* @returns Store - The new store created.
*
* @internal
*/
export function createExperienceControlsStore(): Store<ExperienceControlsState> {
const store: SafeStore<
ExperienceControlsState,
ExperienceControlsGetters,
ExperienceControlsMutations,
ExperienceControlsActions
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
> = new Store(experienceControlsXStoreModule as any);

return store;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable max-len */
import Vue from 'vue';
import { setStatus } from '../../../store/utils/status-store.utils';
import { fetchAndSaveExperienceControlsResponse } from './actions/fetch-and-save-experience-controls.action';
import {
cancelFetchAndSaveControls,
fetchAndSaveExperienceControlsResponse
} from './actions/fetch-and-save-experience-controls.action';
import { fetchExperienceControlsResponse } from './actions/fetch-experience-controls.action';
import { experienceControlsRequest } from './getters/experience-controls-results-request.getter';
import { ExperienceControlsXStoreModule } from './types';
Expand Down Expand Up @@ -36,6 +39,7 @@ export const experienceControlsXStoreModule: ExperienceControlsXStoreModule = {
},
actions: {
fetchExperienceControlsResponse,
fetchAndSaveExperienceControlsResponse
fetchAndSaveExperienceControlsResponse,
cancelFetchAndSaveControls
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ export interface ExperienceControlsActions {
* @param request - The request to fetch the experience controls.
*/
fetchAndSaveExperienceControlsResponse(request: ExperienceControlsRequest | null): void;

/**
* Cancels the {@link ExperienceControlsActions.fetchAndSaveExperienceControlsResponse}.
*/
cancelFetchAndSaveControls: () => void;
}

/**
Expand Down

0 comments on commit 98acc0f

Please sign in to comment.