Skip to content

Commit

Permalink
Merge pull request #112 from wmde/conductor-tests
Browse files Browse the repository at this point in the history
BannerConductor Tests
  • Loading branch information
Abban authored May 26, 2023
2 parents 2192a15 + 35324fd commit 148f0d7
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 27 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
"license": "MIT",
"repository": "https://github.com/wmde/fundraising-banners",
"dependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@vue/eslint-config-typescript": "^11.0.3",
"format-number": "^3.0.0",
"fundraising-frontend-content": "github:wmde/fundraising-frontend-content#production",
"keen-slider": "^6.8.5",
Expand All @@ -34,6 +32,7 @@
"devDependencies": {
"@types/jsdom": "^21.1.1",
"@vitejs/plugin-vue": "^4.2.1",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/test-utils": "^2.3.2",
"@vue/tsconfig": "^0.1.3",
"css-loader": "^6.7.1",
Expand All @@ -49,6 +48,7 @@
"npm-check-updates": "^16.7.10",
"npm-run-all": "^4.1.5",
"rimraf": "^5.0.1",
"@rushstack/eslint-patch": "^1.2.0",
"sass": "^1.62.1",
"sass-loader": "^13.2.2",
"string-replace-loader": "^3.1.0",
Expand Down
95 changes: 74 additions & 21 deletions test/components/BannerConductor/BannerConductor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import BannerConductor from '@src/components/BannerConductor/BannerConductor.vue
import { PageStub } from '@test/fixtures/PageStub';
import { ResizeHandlerStub } from '@test/fixtures/ResizeHandlerStub';
import { ImpressionCountStub } from '@test/fixtures/ImpressionCountStub';
import { defineComponent, h, markRaw, nextTick } from 'vue';
import { defineComponent, markRaw, nextTick } from 'vue';
import { newBannerStateMachine } from '@src/components/BannerConductor/StateMachine/BannerStateMachine';
import { BannerStateMachineSpy } from '@test/fixtures/BannerStateMachineSpy';
import { BannerStates } from '@src/components/BannerConductor/StateMachine/BannerStates';
import { Page } from '@src/page/Page';
import { BannerNotShownReasons } from '@src/page/BannerNotShownReasons';
import { LegacyCloseSources } from '@src/tracking/LegacyCloseSources';
import { TrackerStub } from '@test/fixtures/TrackerStub';
import { ReactiveProperty } from '@src/domain/StateMachine/ReactiveProperty';
import { BannerState } from '@src/components/BannerConductor/StateMachine/states/BannerState';
import { CloseEvent } from '@src/tracking/events/CloseEvent';
import { ResizeHandler } from '@src/utils/ResizeHandler';

vi.mock( '@src/components/BannerConductor/StateMachine/BannerStateMachine', async () => {
const actual = await vi.importActual( '@src/components/BannerConductor/StateMachine/BannerStateMachine' );
Expand All @@ -28,7 +29,7 @@ describe( 'BannerConductor.vue', () => {

let stateMachineSpy: BannerStateMachineSpy;

async function getShownBannerWrapper( page: Page|null = null ): Promise<VueWrapper<any>> {
async function getShownBannerWrapper( page: Page|null = null, resizeHandler: ResizeHandler|null = null ): Promise<VueWrapper<any>> {
const banner = defineComponent( {
props: {
bannerState: String
Expand All @@ -37,15 +38,22 @@ describe( 'BannerConductor.vue', () => {
'bannerClosed',
'bannerContentChanged'
],
render() {
return h( 'div', { 'class': 'test-banner', 'innerHTML': 'hello' } );
}
methods: {
onClose() {
this.$emit( 'bannerClosed', new CloseEvent( 'MainBanner', 'closed' ) );
}
},
template: `<div class="test-banner" style="height: 100px">
Hello, world!
<button class="emit-banner-closed" @click="onClose"></button>
<button class="emit-banner-content-changed" @click="$emit( 'bannerContentChanged' )"></button>
</div>`
} );
const wrapper = mount( BannerConductor, {
props: {
page: page ?? new PageStub(),
bannerConfig: { delay: 42, transitionDuration: 5 },
resizeHandler: new ResizeHandlerStub(),
resizeHandler: resizeHandler ?? new ResizeHandlerStub(),
banner: markRaw( banner ),
impressionCount: new ImpressionCountStub()
},
Expand All @@ -62,8 +70,6 @@ describe( 'BannerConductor.vue', () => {
await nextTick();
await vi.runAllTimersAsync();

console.log( wrapper.html() );

return Promise.resolve( wrapper );
}

Expand All @@ -80,25 +86,29 @@ describe( 'BannerConductor.vue', () => {
} );

it( 'runs through correct state flow on mounted', async () => {
await getShownBannerWrapper();
const wrapper = await getShownBannerWrapper();

expect( stateMachineSpy.statesCalled ).toEqual( [
BannerStates.Pending,
BannerStates.Showing,
BannerStates.Visible
] );

expect( wrapper.classes() ).toContain( 'wmde-banner--visible' );
} );

it( 'runs through correct state flow on mounted when there is a reason to not show banner', async () => {
const page = new PageStub();
page.getReasonToNotShowBanner = vi.fn().mockReturnValue( BannerNotShownReasons.SizeIssue );

await getShownBannerWrapper( page );
const wrapper = await getShownBannerWrapper( page );

expect( stateMachineSpy.statesCalled ).toEqual( [
BannerStates.Pending,
BannerStates.NotShown
] );

expect( wrapper.classes() ).toContain( BannerStates.NotShown );
} );

it( 'passes the banner height to the page on load', async () => {
Expand All @@ -107,7 +117,7 @@ describe( 'BannerConductor.vue', () => {

await getShownBannerWrapper( page );

// This will be 0 because we shallow mount the component meaning it has no content
// This will be 0 because jsdom doesn't set this value
expect( page.setSpace ).toHaveBeenCalledWith( 0 );
} );

Expand All @@ -124,21 +134,42 @@ describe( 'BannerConductor.vue', () => {
expect( page.showBanner ).toHaveBeenCalled();
} );

it.todo( 'updates the page with a new height on content change', async () => {
it( 'updates the page with a new height on window resize', async () => {
const page = new PageStub();
page.setSpace = vi.fn().mockReturnValue( page );
const wrapper = await getShownBannerWrapper( page );
const resizeHandler = new ResizeHandlerStub();
const wrapper = await getShownBannerWrapper( page, resizeHandler );

await wrapper.find( '.test-banner' ).trigger( 'banner-content-changed' );
Object.defineProperty( wrapper.element, 'offsetHeight', { value: 100, writable: true, configurable: true } );
resizeHandler.callOnResize();

expect( page.setSpace ).toHaveBeenCalledTimes( 2 );
Object.defineProperty( wrapper.element, 'offsetHeight', { value: 42, writable: true, configurable: true } );
resizeHandler.callOnResize();

expect( page.setSpace ).toHaveBeenCalledTimes( 3 );
expect( page.setSpace ).toHaveBeenCalledWith( 100 );
expect( page.setSpace ).toHaveBeenCalledWith( 42 );
} );

it.todo( 'moves to closed state when donor closes banner', async () => {
it( 'updates the page with a new height on content change', async () => {
const page = new PageStub();
page.setCloseCookieIfNecessary = vi.fn().mockReturnValue( page );
page.setSpace = vi.fn().mockReturnValue( page );
const wrapper = await getShownBannerWrapper( page );
await wrapper.find( 'anonymous-stub' ).trigger( 'banner-closed', { blah: LegacyCloseSources.MaybeLater } );

Object.defineProperty( wrapper.element, 'offsetHeight', { value: 100, writable: true, configurable: true } );
await wrapper.find( '.emit-banner-content-changed' ).trigger( 'click' );

Object.defineProperty( wrapper.element, 'offsetHeight', { value: 42, writable: true, configurable: true } );
await wrapper.find( '.emit-banner-content-changed' ).trigger( 'click' );

expect( page.setSpace ).toHaveBeenCalledTimes( 3 );
expect( page.setSpace ).toHaveBeenCalledWith( 100 );
expect( page.setSpace ).toHaveBeenCalledWith( 42 );
} );

it( 'moves to closed state when donor closes banner', async () => {
const wrapper = await getShownBannerWrapper();
await wrapper.find( '.emit-banner-closed' ).trigger( 'click' );

expect( stateMachineSpy.statesCalled ).toEqual( [
BannerStates.Pending,
Expand All @@ -147,9 +178,31 @@ describe( 'BannerConductor.vue', () => {
BannerStates.Closed
] );

expect( page.setCloseCookieIfNecessary ).toHaveBeenCalledWith( LegacyCloseSources.MaybeLater );
expect( wrapper.classes() ).toContain( BannerStates.Closed );
} );

it.todo( 'moves to closed state when an page event that should hide the banner happens', () => {
it( 'asks the page to set the close cookie when the donor closes banner', async () => {
const page = new PageStub();
page.setCloseCookieIfNecessary = vi.fn().mockReturnValue( page );
const wrapper = await getShownBannerWrapper( page );
await wrapper.find( '.emit-banner-closed' ).trigger( 'click' );

expect( page.setCloseCookieIfNecessary ).toHaveBeenCalledWith( new CloseEvent( 'MainBanner', 'closed' ) );
} );

it( 'moves to closed state when an page event that should hide the banner happens', async () => {
const page = new PageStub();
const wrapper = await getShownBannerWrapper( page );

await page.hideBannerCallback();

expect( stateMachineSpy.statesCalled ).toEqual( [
BannerStates.Pending,
BannerStates.Showing,
BannerStates.Visible,
BannerStates.Closed
] );

expect( wrapper.classes() ).toContain( BannerStates.Closed );
} );
} );
5 changes: 4 additions & 1 deletion test/fixtures/PageStub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { CampaignParameters } from '@src/domain/CampaignParameters';
import { TrackingParameters } from '@src/domain/TrackingParameters';

export class PageStub implements Page {
public hideBannerCallback: () => void;

public getBannerContainer(): string {
return '';
}
Expand All @@ -16,7 +18,8 @@ export class PageStub implements Page {
return this;
}

public onPageEventThatShouldHideBanner(): void {
public onPageEventThatShouldHideBanner( hideBannerListener: () => void ): void {
this.hideBannerCallback = hideBannerListener;
}

public removePageEventListeners(): Page {
Expand Down
6 changes: 4 additions & 2 deletions test/fixtures/ResizeHandlerStub.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ResizeHandler } from '@src/utils/ResizeHandler';

export class ResizeHandlerStub implements ResizeHandler {
public callOnResize: () => void;

public onClose(): void {
}

public onResize(): void {
public onResize( resizeHandler: () => void ): void {
this.callOnResize = resizeHandler;
}

}
3 changes: 2 additions & 1 deletion webpack.production.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const path = require( 'path' );
const campaigns = new CampaignConfig( toml.parse( fs.readFileSync( 'campaign_info.toml', 'utf8' ) ) );

function readWrapperTemplate( name ) {
return fs.readFileSync( './webpack/wikitext_templates/' + name + '.hbs', 'utf8' );
// eslint-disable-next-line security/detect-non-literal-fs-filename
return fs.readFileSync( `./webpack/wikitext_templates/${name}.hbs`, 'utf8' );
}

module.exports = ( env ) => {
Expand Down

0 comments on commit 148f0d7

Please sign in to comment.