Skip to content
This repository has been archived by the owner on Mar 23, 2022. It is now read-only.

Commit

Permalink
ADD - Analysis lookup control tests
Browse files Browse the repository at this point in the history
Signed-off-by: RaenonX <[email protected]>
  • Loading branch information
RaenonX committed May 27, 2021
1 parent 7a5f8ec commit 32620a9
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/components/elements/common/anchor/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const scrollToAnchor = (history: History) => {


export const scrollToElement = (element: HTMLElement) => {
// FIXME: possible duplicate of `scrollToTop`?
const titleNav = document.getElementById(titleNavBarId);

const anchorHeight = element.getBoundingClientRect().top + window.pageYOffset;
Expand Down
320 changes: 320 additions & 0 deletions src/components/elements/posts/analysis/lookup/main.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
import React from 'react';

import {act, fireEvent, screen, waitFor} from '@testing-library/react';

import {renderReact} from '../../../../../../test/render/main';
import {SupportedLanguages} from '../../../../../api-def/api/other/lang';
import {UnitType} from '../../../../../api-def/api/other/unit';
import {
AnalysisLookupAnalyses,
AnalysisLookupLandingResponse,
AnalysisLookupResponse,
} from '../../../../../api-def/api/post/analysis/response';
import {ApiResponseCode} from '../../../../../api-def/api/responseCode';
import {translation as translationEN} from '../../../../../i18n/translations/en/translation';
import * as scrollUtils from '../../../../../utils/scroll';
import {ApiRequestSender} from '../../../../../utils/services/api/requestSender';
import {GoogleAnalytics} from '../../../../../utils/services/ga';
import {InputData} from './in/types';
import {AnalysisPostLookup} from './main';

describe('Analysis lookup page', () => {
let fnScroll: jest.SpyInstance;
let fnGetLookup: jest.SpyInstance<Promise<AnalysisLookupResponse>, [string, SupportedLanguages]>;
let fnGetLookupLanding: jest.SpyInstance<Promise<AnalysisLookupLandingResponse>, [string, SupportedLanguages]>;
let fnGaAnalysisLookup: jest.SpyInstance;

const analyses: AnalysisLookupAnalyses = {
10950101: {
lang: SupportedLanguages.CHT,
type: UnitType.CHARACTER,
unitId: 10950101,
viewCount: 777,
modifiedEpoch: 3,
publishedEpoch: 1,
},
10950102: {
lang: SupportedLanguages.CHT,
type: UnitType.CHARACTER,
unitId: 10950102,
viewCount: 888,
modifiedEpoch: 6,
publishedEpoch: 4,
},
10950301: {
lang: SupportedLanguages.CHT,
type: UnitType.CHARACTER,
unitId: 10950301,
viewCount: 999,
modifiedEpoch: 6,
publishedEpoch: 4,
},
};

const lookupResponseNoAnalyses: AnalysisLookupResponse = {
code: ApiResponseCode.SUCCESS,
isAdmin: false,
success: false,
analyses: {},
};

const lookupResponseHasAnalyses: AnalysisLookupResponse = {
...lookupResponseNoAnalyses,
analyses,
};

const lookupLandingResponse: AnalysisLookupLandingResponse = {
code: ApiResponseCode.SUCCESS,
success: false,
isAdmin: false,
analyses: [analyses[10950102], analyses[10950301]],
};

beforeEach(() => {
fnScroll = jest.spyOn(scrollUtils, 'scrollToTop').mockImplementation(() => void 0);
fnGetLookup = jest.spyOn(ApiRequestSender, 'analysisLookup');
fnGetLookupLanding = jest.spyOn(ApiRequestSender, 'analysisLookupLanding')
.mockImplementation(async () => lookupLandingResponse);
fnGaAnalysisLookup = jest.spyOn(GoogleAnalytics, 'analysisLookup');
});

it('searches and scrolls if found', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseHasAnalyses);
renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
});

const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.click(searchButton);
});

await waitFor(async () => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(fnScroll).toHaveBeenCalledTimes(1);
expect(screen.queryByAltText('Gala Leonidas')).toBeInTheDocument();
});
});

it('searches and scrolls even if not found', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
});

const keywordInput = screen.getByPlaceholderText(translationEN.misc.searchKeyword);
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.change(keywordInput, {target: {value: 'AAA'}});
});
act(() => {
fireEvent.click(searchButton);
});

await waitFor(() => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(fnScroll).toHaveBeenCalledTimes(1);
});
expect(screen.queryByAltText('Gala Leonidas')).not.toBeInTheDocument();
});

it('scrolls on not found, also scrolls on found', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
const {rerender} = renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
});

const keywordInput = screen.getByPlaceholderText(translationEN.misc.searchKeyword);
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.change(keywordInput, {target: {value: 'AAA'}});
});
act(() => {
fireEvent.click(searchButton);
});

await waitFor(() => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(fnScroll).toHaveBeenCalledTimes(1);
});
expect(screen.queryByText('Gala Leonidas')).not.toBeInTheDocument();

fnGetLookup.mockImplementationOnce(async () => lookupResponseHasAnalyses);
rerender();

expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
act(() => {
fireEvent.change(keywordInput, {target: {value: ''}});
});
act(() => {
fireEvent.click(searchButton);
});

await waitFor(async () => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(screen.queryByAltText('Gala Leonidas')).toBeInTheDocument();
});
});

it('searches by type', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
expect(screen.queryByAltText('elementsFLAME')).toBeInTheDocument();
});

const flameElemButton = screen.getByAltText('elementsFLAME');
act(() => {
fireEvent.click(flameElemButton);
});
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.click(searchButton);
});

await waitFor(async () => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(screen.queryByAltText('Gala Leonidas')).toBeInTheDocument();
});
expect(screen.queryByAltText('Karina')).not.toBeInTheDocument();
});

it('searches by weapon type', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
expect(screen.queryByAltText('weaponTypesAXE')).toBeInTheDocument();
});

const flameElemButton = screen.getByAltText('weaponTypesAXE');
act(() => {
fireEvent.click(flameElemButton);
});
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.click(searchButton);
});

await waitFor(async () => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(screen.queryByAltText('Karina')).toBeInTheDocument();
});
expect(screen.queryByAltText('Gala Leonidas')).not.toBeInTheDocument();
});

it('searches by keyword', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
});

const keywordInput = screen.getByPlaceholderText(translationEN.misc.searchKeyword);
act(() => {
fireEvent.change(keywordInput, {target: {value: 'Karina'}});
});
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.click(searchButton);
});

await waitFor(async () => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
});
await waitFor(async () => {
expect(screen.queryByAltText('Karina')).toBeInTheDocument();
});
expect(screen.queryByAltText('Gala Leonidas')).not.toBeInTheDocument();
});

it('sends GA event', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
renderReact(() => <AnalysisPostLookup/>);

await waitFor(() => {
expect(fnGetLookupLanding).toHaveBeenCalledTimes(1);
expect(screen.queryByAltText('elementsWATER')).toBeInTheDocument();
expect(screen.queryByAltText('elementsWIND')).toBeInTheDocument();
expect(screen.queryByAltText('weaponTypesAXE')).toBeInTheDocument();
});

const waterElemButton = screen.getByAltText('elementsWATER');
act(() => {
fireEvent.click(waterElemButton);
});
const windElemButton = screen.getByAltText('elementsWIND');
act(() => {
fireEvent.click(windElemButton);
});
const axeButton = screen.getByAltText('weaponTypesAXE');
act(() => {
fireEvent.click(axeButton);
});
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.click(searchButton);
});

const expectedInput: InputData = {
types: [],
elements: [2, 3],
weaponTypes: [4],
keyword: '',
};
await waitFor(async () => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
expect(fnGaAnalysisLookup).toHaveBeenCalledTimes(1);
expect(fnGaAnalysisLookup).toHaveBeenCalledWith(expectedInput);
});
await waitFor(async () => {
expect(screen.queryByAltText('Karina')).toBeInTheDocument();
});
expect(screen.queryByAltText('Gala Leonidas')).not.toBeInTheDocument();
});

it('shows error if no units matching the search criteria', async () => {
fnGetLookup.mockImplementationOnce(async () => lookupResponseNoAnalyses);
renderReact(() => <AnalysisPostLookup/>);

const keywordInput = screen.getByPlaceholderText(translationEN.misc.searchKeyword);
act(() => {
fireEvent.change(keywordInput, {target: {value: 'AAAA'}});
});
const searchButton = screen.getByText(translationEN.misc.search);
act(() => {
fireEvent.click(searchButton);
});

await waitFor(() => {
expect(fnGetLookup).toHaveBeenCalledTimes(1);
expect(fnScroll).toHaveBeenCalledTimes(1);
expect(screen.queryByText(translationEN.posts.analysis.error.noResult)).toBeInTheDocument();
});
const errorText = screen.getByText(translationEN.posts.analysis.error.noResult);
expect(errorText).toHaveClass('text-danger');
expect(errorText).toHaveClass('text-center');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const AnalysisPostLookup = () => {
return (
<>
<AnalysisLookupLanding analyses={lookupLanding.data?.analyses || []}/>
<hr/>
<AnalysisLookupInput
isAdmin={lookupLanding.data?.isAdmin || false}
onSearchRequested={(data) => () => {
Expand Down
15 changes: 12 additions & 3 deletions src/components/elements/posts/analysis/lookup/out/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type AnalysisLookupOutputProps = {
}

export const AnalysisLookupOutput = ({inputData}: AnalysisLookupOutputProps) => {
const {lang} = useI18n();
const {t, lang} = useI18n();

const rowElem = React.useRef<HTMLDivElement>(null);

Expand All @@ -37,15 +37,24 @@ export const AnalysisLookupOutput = ({inputData}: AnalysisLookupOutputProps) =>
'Failed to fetch analysis meta.',
);

// Scroll after input data has changed
React.useEffect(() => {
scrollToTop(rowElem);
}, [inputData]);

fetchAnalysisMeta();

const unitInfoFiltered = getUnitInfo(inputData, charaInfo, dragonInfo);
// Split to prioritize the units that have analysis
const unitInfoHasAnalysis = unitInfoFiltered.filter((info) => info.id in analysisMeta.data.analyses);
const unitInfoNoAnalysis = unitInfoFiltered.filter((info) => !(info.id in analysisMeta.data.analyses));

if (unitInfoFiltered.length) {
scrollToTop(rowElem);
if (inputData && charaInfo.length && dragonInfo.length && !unitInfoFiltered.length) {
return (
<h5 className="text-danger text-center">
{t((t) => t.posts.analysis.error.noResult)}
</h5>
);
}

return (
Expand Down
6 changes: 5 additions & 1 deletion src/components/elements/posts/analysis/lookup/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import {
import {InputData} from './in/types';

export const getUnitInfo = (
inputData: InputData,
inputData: InputData | undefined,
charaInfo: CharaInfo,
dragonInfo: DragonInfo,
): Array<UnitInfoData> => {
if (!inputData) {
return [];
}

const ret: Array<UnitInfoData> = [];

const isUnitElementMatch = (unit: UnitInfoDataBase) => (
Expand Down
2 changes: 1 addition & 1 deletion src/components/pages/posts/analysis/lookup.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import {useI18n} from '../../../../i18n/hook';
import {AnalysisPostLookup} from '../../../elements/posts/analysis/lookup/lookup';
import {AnalysisPostLookup} from '../../../elements/posts/analysis/lookup/main';
import {PageProps} from '../../props';


Expand Down
Loading

0 comments on commit 32620a9

Please sign in to comment.