Skip to content

Commit

Permalink
Move postSuccess into DonationSubmitter
Browse files Browse the repository at this point in the history
  • Loading branch information
wwahammy committed Oct 19, 2022
1 parent 3e2f696 commit 5b2d98b
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 58 deletions.
1 change: 0 additions & 1 deletion app/javascript/common/Callbacks/run.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// License: LGPL-3.0-or-later
import type { CallbackClass } from "./types";


/**
* A very simple function for conditionally running callbacks. Move into own file because we can mock it for CallbackController
* @param input The input properties to every callback
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// License: LGPL-3.0-or-later
import last from 'lodash/last';
import isEqual from 'lodash/isEqual'
import findLast from 'lodash/findLast';

/**
* EventStack is a simple class that records objects based upon their type property. One way of using it is
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// License: LGPL-3.0-or-later
import PlausibleCallback from './PlausibleCallback';


describe('PlausibleCallback', () => {
describe('.canRun', () => {
it('false when getPlausible is undefined', () => {
const c = new PlausibleCallback({ props: {}} as any);
expect(c.canRun()).toEqual(false)
})

it('false when getPlausible returns undefined', () => {
const c = new PlausibleCallback({ props: {getPlausible: ():any => undefined}} as any);
expect(c.canRun()).toEqual(false)
})

it('true when returns plausible function', () => {
const realPlausibleFunc = jest.fn();
const c = new PlausibleCallback({ props: {getPlausible: ():any => realPlausibleFunc}} as any);
expect(c.canRun()).toEqual(true);
})
})

describe('.run', () => {
function build(result?:{charge?:{amount?:number}}) {
const realPlausibleFunc = jest.fn();
return {
plausible: realPlausibleFunc,
obj: new PlausibleCallback({ props: {getPlausible: ():any => realPlausibleFunc}, result} as any)
};

}

it('calls plausible with no amount when result is undefined', async () => {
const {plausible, obj} = build();
await obj.run();
expect(plausible).toHaveBeenCalledWith('payment_succeeded', {
props: {
amount: undefined,
}
});
})

it('calls plausible with no amount when charge is undefined', async () => {
const {plausible, obj} = build({});
await obj.run();
expect(plausible).toHaveBeenCalledWith('payment_succeeded', {
props: {
amount: undefined,
}
});
})

it('calls plausible with no amount when charge.amount is undefined', async () => {
const {plausible, obj} = build({charge:{}});
await obj.run();
expect(plausible).toHaveBeenCalledWith('payment_succeeded', {
props: {
amount: undefined,
}
});
})

it('calls plausible with amount/100 when charge.amount is defined', async () => {
const {plausible, obj} = build({charge:{amount: 1000}});
await obj.run();
expect(plausible).toHaveBeenCalledWith('payment_succeeded', {
props: {
amount: 10,
}
});
})
});

describe('.catchError', () => {
it('does not rethrow errors', () => {
const c = new PlausibleCallback({} as any);
expect(() => c.catchError(new Error())).not.toThrow();
})
})
});
36 changes: 36 additions & 0 deletions client/js/nonprofits/donate/DonationSubmitter/PlausibleCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Callback } from "../../../../../app/javascript/common/Callbacks";
import DonationSubmitter from './';

// License: LGPL-3.0-or-later
export interface PlausibleFunction {
(eventType: string, val: any): void
}

export interface GetPlausible {

(): PlausibleFunction | undefined
}


export default class PlausibleCallback extends Callback<DonationSubmitter> {

private get plausibleFunction(): PlausibleFunction {
return this.props.props.getPlausible()
}
canRun(): boolean {
return !!(this.props.props.getPlausible && this.props.props.getPlausible())
}

run(): void {
this.plausibleFunction('payment_succeeded', {
props: {
amount: this.props.result?.charge?.amount && (this.props.result.charge.amount / 100)
}
});
}

catchError(e: unknown): void {
console.log(e);
}

}
100 changes: 82 additions & 18 deletions client/js/nonprofits/donate/DonationSubmitter/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
// License: LGPL-3.0-or-later

import DonationSubmitter from '.';
import run from '../../../../../app/javascript/common/Callbacks/run';
import PlausibleCallback from './PlausibleCallback';
import {waitFor} from '@testing-library/dom';

jest.mock('../../../../../app/javascript/common/Callbacks/run', () => jest.fn());

describe('DonationSubmitter', () => {


function SetupDonationSubmitter(updated=jest.fn()) {
beforeEach(() => {
jest.clearAllMocks();
})

function SetupDonationSubmitter(updated=jest.fn(), getPlausible=jest.fn()) {
const runCallbacks = run as jest.Mock;

const ret = {
submitter: new DonationSubmitter(),
submitter: new DonationSubmitter({getPlausible}),
updated,
getPlausible,
runCallbacks,
};

ret.submitter.addEventListener('updated', ret.updated)


return ret;

}

it('has only one postSuccess callback', () => {
const ret = SetupDonationSubmitter()
expect(Array.from(ret.submitter.callbacks().keys())).toStrictEqual(['success'])

expect(ret.submitter.callbacks('success')).toStrictEqual({before: [], after: [PlausibleCallback]})
})

describe("before anything happens", () => {

function prepare(): ReturnType<typeof SetupDonationSubmitter> {
Expand Down Expand Up @@ -52,10 +73,16 @@ describe('DonationSubmitter', () => {
const {updated} = prepare()
expect(updated).not.toHaveBeenCalled()
})

it('has not ran callbacks', () => {
const {runCallbacks} = prepare();
expect(runCallbacks).not.toHaveBeenCalled();

});
})

describe("when beginSubmit and then savedCard", () => {

function prepare(): ReturnType<typeof SetupDonationSubmitter> {
const func = jest.fn(() => {
})
Expand All @@ -67,7 +94,7 @@ describe('DonationSubmitter', () => {

it('is loading', () => {
const {submitter: state} = prepare()

expect(state.loading).toBe(true);
})

Expand Down Expand Up @@ -106,10 +133,16 @@ describe('DonationSubmitter', () => {

expect(updated).toHaveBeenCalledTimes(2);
})

it('has not ran callbacks', () => {
const {runCallbacks} = prepare();
expect(runCallbacks).not.toHaveBeenCalled();

});
})

describe("when beginSubmit and then completed", () => {

const donationResult = { };
function prepare(): ReturnType<typeof SetupDonationSubmitter> {
const mocked = SetupDonationSubmitter();
Expand All @@ -122,7 +155,7 @@ describe('DonationSubmitter', () => {

it('is loading', () => {
const {submitter: state} = prepare()

expect(state.loading).toBe(false);
})

Expand Down Expand Up @@ -156,17 +189,23 @@ describe('DonationSubmitter', () => {
expect(updated).toHaveBeenCalledTimes(3);
})

it('calling completed twice only fires it once', () => {
it('calling completed twice only fires it once', async () => {
const {submitter: state, updated} = prepare();
state.reportCompleted(donationResult);

expect(updated).toHaveBeenCalledTimes(3)
})

it('has ran callbacks', async () => {
const {runCallbacks, submitter:state} = prepare();
expect(runCallbacks).toHaveBeenCalledWith(state, []);

await waitFor(() => expect(runCallbacks).toHaveBeenCalledWith(state, [PlausibleCallback]))
});
})

describe("when beginSubmit and then errored", () => {

const error = "Error message"

function prepare(): ReturnType<typeof SetupDonationSubmitter> {
Expand Down Expand Up @@ -219,8 +258,14 @@ describe('DonationSubmitter', () => {
state.reportError(error);

expect(updated).toHaveBeenCalledTimes(2);

})

it('has not ran callbacks', () => {
const {runCallbacks} = prepare();
expect(runCallbacks).not.toHaveBeenCalled();

});
})

describe("when savedCard and then errored", () => {
Expand Down Expand Up @@ -278,12 +323,18 @@ describe('DonationSubmitter', () => {

expect(updated).toHaveBeenCalledTimes(3);
})

it('has not ran callbacks', () => {
const {runCallbacks} = prepare();
expect(runCallbacks).not.toHaveBeenCalled();

});
});


describe("when errored and then re-attempted", () => {
const error = "Error message";

function prepare(): ReturnType<typeof SetupDonationSubmitter> {
const mocked = SetupDonationSubmitter();
mocked.submitter.reportBeginSubmit();
Expand All @@ -295,7 +346,7 @@ describe('DonationSubmitter', () => {

it('is loading', () => {
const {submitter: state} = prepare()

expect(state.loading).toBe(true);
})

Expand Down Expand Up @@ -329,13 +380,18 @@ describe('DonationSubmitter', () => {
expect(updated).toHaveBeenCalledTimes(4);
});

it('has not ran callbacks', () => {
const {runCallbacks} = prepare();
expect(runCallbacks).not.toHaveBeenCalled();

});
})

describe("when errored and then re-attempted", () => {
describe("when errored and then succeeded", () => {
const error = "Error message";
const donationResult:any = { charge: undefined };
function prepare(): ReturnType<typeof SetupDonationSubmitter> {
const mocked = SetupDonationSubmitter(jest.fn());
const mocked = SetupDonationSubmitter(jest.fn(), jest.fn());
mocked.submitter.reportBeginSubmit();
mocked.submitter.reportSavedCard();
mocked.submitter.reportError(error);
Expand All @@ -347,7 +403,7 @@ describe('DonationSubmitter', () => {

it('is loading', () => {
const {submitter: state} = prepare()

expect(state.loading).toBe(false);
})

Expand Down Expand Up @@ -380,7 +436,15 @@ describe('DonationSubmitter', () => {

expect(updated).toHaveBeenCalledTimes(6);
});

it('has ran callbacks', async () => {
const {runCallbacks, submitter:state} = prepare();
expect(runCallbacks).toHaveBeenCalledWith(state, []);
await waitFor(() => expect(runCallbacks).toHaveBeenCalledWith(state, [PlausibleCallback]))


});
})


})
Loading

0 comments on commit 5b2d98b

Please sign in to comment.