Skip to content

Commit

Permalink
Merge branch 'master' into feat/SFINT-3353
Browse files Browse the repository at this point in the history
  • Loading branch information
louis-bompart committed Jul 30, 2020
2 parents b5ceacf + 664ee53 commit 3ed0df4
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 9 deletions.
5 changes: 3 additions & 2 deletions src/components/ActionButton/StatefulActionButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ export class StatefulActionButton {
);
return;
}
this.currentState.onStateExit?.apply(this);
state.onStateEntry?.apply(this);
const [oldStateExit, newStateEntry] = [this.currentState.onStateExit, state.onStateEntry];
this.innerActionButton.updateIcon(state.icon);
this.innerActionButton.updateTooltip(state.tooltip);
this.currentState = state;
oldStateExit?.call(this);
newStateEntry?.call(this);
}

/**
Expand Down
39 changes: 32 additions & 7 deletions tests/components/ActionButton/StatefulActionButton.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import { StatefulActionButton, StatefulActionButtonState } from '../../../src/components/ActionButton/StatefulActionButton';
import { createSandbox, SinonSpy, SinonSandbox } from 'sinon';
import { ActionButton } from '../../../src/components/ActionButton/ActionButton';

describe('StatefulActionButton', () => {
let testSubject: StatefulActionButton;
let sandbox: SinonSandbox;
let spyConsoleWarn: SinonSpy;
const createSpiedState: (stateName: string) => [StatefulActionButtonState, SinonSpy, SinonSpy] = (stateName: string) => {
const stateEntrySpy = sandbox.spy();
const stateExitSpy = sandbox.spy();
const createSpiedState: (
stateName: string,
onStateEntry?: (this: StatefulActionButton) => void,
onStateExit?: (this: StatefulActionButton) => void
) => [StatefulActionButtonState, SinonSpy, SinonSpy] = (
stateName: string,
onStateEntry?: (this: StatefulActionButton) => void,
onStateExit?: (this: StatefulActionButton) => void
) => {
const stateEntrySpy = onStateEntry ? sandbox.spy(onStateEntry) : sandbox.spy();
const stateExitSpy = onStateExit ? sandbox.spy(onStateExit) : sandbox.spy();
const state: StatefulActionButtonState = {
name: stateName,
icon: 'foo',
title: 'bar',
onStateEntry: stateEntrySpy,
onStateExit: stateExitSpy,
onStateEntry:
onStateEntry ??
function () {
stateEntrySpy();
},
onStateExit:
onStateExit ??
function () {
stateExitSpy();
},
};

return [state, stateEntrySpy, stateExitSpy];
Expand Down Expand Up @@ -127,23 +144,31 @@ describe('StatefulActionButton', () => {
let targetState: StatefulActionButtonState;
let onInitialStateExitSpy: SinonSpy;
let onTargetStateEntrySpy: SinonSpy;
let updateIconSpy: SinonSpy;
let updateTooltipSpy: SinonSpy;

function expectUnsuccesfulTransition() {
expect(testSubject.getCurrentState()).toBe(initialState);
expect(onInitialStateExitSpy.called).toBeFalse();
expect(onTargetStateEntrySpy.called).toBeFalse();
expect(updateIconSpy.called).toBeFalse();
expect(updateTooltipSpy.called).toBeFalse();
}

function expectSuccesfulTransition() {
expect(spyConsoleWarn.called).toBeFalse();
expect(testSubject.getCurrentState()).toBe(targetState);
expect(onInitialStateExitSpy.called).toBeTrue();
expect(onTargetStateEntrySpy.called).toBeTrue();
expect(updateIconSpy.called).toBeTrue();
expect(updateTooltipSpy.called).toBeTrue();
expect(onInitialStateExitSpy.calledAfter(updateTooltipSpy)).toBeTrue();
expect(onTargetStateEntrySpy.calledAfter(onInitialStateExitSpy)).toBeTrue();
}

beforeEach(() => {
[initialState, , onInitialStateExitSpy] = createSpiedState('initialState');
[targetState, onTargetStateEntrySpy] = createSpiedState('targetState');
updateIconSpy = sandbox.spy(ActionButton.prototype, 'updateIcon');
updateTooltipSpy = sandbox.spy(ActionButton.prototype, 'updateTooltip');
});

describe('if the state given in parameter is not in the options.state', () => {
Expand Down
106 changes: 106 additions & 0 deletions tests/components/ActionButton/ToggleActionButton.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as icons from '../../../src/utils/icons';
import { ActionButton } from '../../../src/components/ActionButton/ActionButton';
import { IComponentOptions } from 'coveo-search-ui';
import { IToggleableButtonOptions } from '../../../src/components/ActionButton/ToggleableButton';
import { StatefulActionButton } from '../../../src/components/ActionButton/StatefulActionButton';
import { defer } from '../../utils';

describe('ToggleActionButton', () => {
let sandbox: SinonSandbox;
Expand All @@ -16,6 +18,7 @@ describe('ToggleActionButton', () => {
let deactivateSpy: SinonSpy;
let updateIconSpy: SinonSpy;
let updateTooltipSpy: SinonSpy;
let switchToSpy: SinonSpy;

beforeAll(() => {
sandbox = createSandbox();
Expand All @@ -25,6 +28,7 @@ describe('ToggleActionButton', () => {
deactivateSpy = sandbox.spy();
updateIconSpy = sandbox.spy(<any>ActionButton.prototype, 'updateIcon');
updateTooltipSpy = sandbox.spy(<any>ActionButton.prototype, 'updateTooltip');
switchToSpy = sandbox.spy(<any>StatefulActionButton.prototype, 'switchTo');
});

beforeEach(() => {
Expand Down Expand Up @@ -151,4 +155,106 @@ describe('ToggleActionButton', () => {
});
});
});

describe('event-loop test', () => {
let eventCompletionPromises: Array<Promise<void>>;
beforeEach(() => {
eventCompletionPromises = [];
});

describe(`if activated triggers an event that would activate the button. `, () => {
const activateEvent = 'activate-event';

beforeEach(() => {
const activateWithEvent: (this: ToggleActionButton) => void = function () {
const deferred = defer();
eventCompletionPromises.push(deferred.promise);
this.element.dispatchEvent(new CustomEvent(activateEvent, { detail: deferred.resolve }));
};
options.activate = activateWithEvent;
testSubject = createToggleButton(options);
testSubject.element.addEventListener(activateEvent, (e: CustomEvent<() => void>) => {
testSubject.setActivated(true);
e.detail();
});
});

describe('if already activated', () => {
beforeEach(() => {
testSubject.setActivated(true);
sandbox.reset();
});

it('should not call switchTo when setActivated is called with true', async () => {
testSubject.setActivated(true);
for (let index = 0; index < eventCompletionPromises.length; index++) {
await eventCompletionPromises[index];
}
expect(switchToSpy.called).toBeFalse();
});
});

describe('if not activated', () => {
beforeEach(() => {
testSubject.setActivated(false);
sandbox.reset();
});

it('should call switchTo only once when setActivated is called with true', async () => {
testSubject.setActivated(true);
for (let index = 0; index < eventCompletionPromises.length; index++) {
await eventCompletionPromises[index];
}
expect(switchToSpy.calledOnce).toBeTrue();
});
});
});

describe(`if deactivated triggers an event that would deactivate the button. `, () => {
const deactivateEvent = 'deactivate-event';
beforeEach(() => {
const deactivateWithEvent: (this: ToggleActionButton) => void = function () {
const deferred = defer();
eventCompletionPromises.push(deferred.promise);
this.element.dispatchEvent(new CustomEvent(deactivateEvent, { detail: deferred.resolve }));
};
options.deactivate = deactivateWithEvent;
testSubject = createToggleButton(options);
testSubject.element.addEventListener(deactivateEvent, (e: CustomEvent<() => void>) => {
testSubject.setActivated(false);
e.detail();
});
});

describe('if already deactivated', () => {
beforeEach(() => {
testSubject.setActivated(false);
sandbox.reset();
});

it('should not call switchTo when setActivated is called with false', async () => {
testSubject.setActivated(false);
for (let index = 0; index < eventCompletionPromises.length; index++) {
await eventCompletionPromises[index];
}
expect(switchToSpy.called).toBeFalse();
});
});

describe('if activated', () => {
beforeEach(() => {
testSubject.setActivated(true);
sandbox.reset();
});

it('should call switchTo only once when setActivated is called with false', async () => {
testSubject.setActivated(false);
for (let index = 0; index < eventCompletionPromises.length; index++) {
await eventCompletionPromises[index];
}
expect(switchToSpy.calledOnce).toBeTrue();
});
});
});
});
});
17 changes: 17 additions & 0 deletions tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,20 @@ export function fakeUserProfileModel(root: HTMLElement, sandbox: SinonSandbox) {
(root as any)[`Coveo${UserProfileModel.ID}`] = sandbox.createStubInstance(UserProfileModel);
return (root as any)[`Coveo${UserProfileModel.ID}`];
}

/**
* Create a deferred promise
*/
export function defer<T = void>() {
let resolve: (arg?: T) => void;
let reject: (arg: any) => void;
const promise = new Promise<T>((p_resolve, p_reject) => {
resolve = p_resolve;
reject = p_reject;
});
return {
resolve,
reject,
promise,
};
}

0 comments on commit 3ed0df4

Please sign in to comment.