-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ci: validate pull request titles this will allow easy merging of squash commits to inform versioning * style: lint jest as recommended * chore: minimize what's published to npm * feat: load square.js * feat: expose top-level payments factory method * feat: use app id to load script from correct env should prevent class of errors where developers try to use sandbox application id with production script src and vice versa * test: load and payments
- Loading branch information
Max Beatty
authored
Mar 11, 2021
1 parent
c25dfee
commit 3da504f
Showing
12 changed files
with
280 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
name: Semantic Release | ||
|
||
on: | ||
pull_request_target: | ||
types: | ||
- opened | ||
- edited | ||
- synchronize | ||
|
||
jobs: | ||
check_pr_title: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Validate Pull Request title | ||
uses: amannn/[email protected] | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.github | ||
.husky |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
export function payments(): void { | ||
console.log('🔜'); | ||
} | ||
export * from './payments'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import * as Load from './load'; | ||
|
||
describe('Load', () => { | ||
describe('loadSquare', () => { | ||
it('exports loadSquare', () => { | ||
expect(Load).toHaveProperty('loadSquare'); | ||
}); | ||
|
||
it('memoizes loadPromise', () => { | ||
const src = 'https://websdk.squarecdn.com/v0/square.js'; | ||
const p1 = Load.loadSquare(src); | ||
const p2 = Load.loadSquare(src); | ||
|
||
expect(p1).toStrictEqual(p2); | ||
}); | ||
|
||
// hard to unit test because of jsdom behaviors. better to trust integration tests | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/// <reference path='../types/index.d.ts' /> | ||
|
||
function findScript(src: string): HTMLScriptElement | null { | ||
return document.querySelector<HTMLScriptElement>(`script[src="${src}"]`); | ||
} | ||
|
||
function injectScript(src: string): HTMLScriptElement { | ||
const headOrBody = document.head || document.body; | ||
|
||
if (!headOrBody) { | ||
throw new Error('Square.js requires a <body> or <head> element.'); | ||
} | ||
|
||
const script = document.createElement('script'); | ||
script.src = src; | ||
|
||
headOrBody.appendChild(script); | ||
|
||
return script; | ||
} | ||
|
||
let loadPromise: Promise<Square | null> | null = null; | ||
|
||
export function loadSquare(src: string): Promise<Square | null> { | ||
if (loadPromise !== null) { | ||
return loadPromise; | ||
} | ||
|
||
loadPromise = new Promise((resolve, reject) => { | ||
if (typeof window === 'undefined') { | ||
// Resolve to null when imported server side. This makes the module safe to import in an isomorphic code base. | ||
resolve(null); | ||
return; | ||
} | ||
|
||
if (window.Square) { | ||
resolve(window.Square); | ||
return; | ||
} | ||
|
||
try { | ||
let script = findScript(src); | ||
|
||
if (!script) { | ||
script = injectScript(src); | ||
} | ||
|
||
script.addEventListener('load', () => { | ||
if (window.Square) { | ||
resolve(window.Square); | ||
} else { | ||
reject(new Error('Square.js failed to load properly.')); | ||
} | ||
}); | ||
|
||
script.addEventListener('error', () => { | ||
reject(new Error('Error occurred while loading Square.js')); | ||
}); | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}); | ||
|
||
return loadPromise; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import * as Payments from './payments'; | ||
import { loadSquare } from './load'; | ||
|
||
jest.mock('./load'); | ||
|
||
const mockLoadSquare = loadSquare as jest.MockedFunction<typeof loadSquare>; | ||
|
||
describe('Payments', () => { | ||
beforeEach(() => { | ||
mockLoadSquare.mockClear(); | ||
}); | ||
|
||
describe('payments', () => { | ||
it('exports payments', () => { | ||
expect(Payments).toHaveProperty('payments'); | ||
}); | ||
|
||
it('throws if application id is invalid and has no override', async () => { | ||
await expect(Payments.payments('junk-app-id')).rejects.toThrow( | ||
"The Payment 'applicationId' option is not in the correct format." | ||
); | ||
expect(mockLoadSquare).not.toBeCalled(); | ||
}); | ||
|
||
it('allows overriding script src', async () => { | ||
mockLoadSquare.mockResolvedValueOnce(null); | ||
|
||
const testSrc = 'https://square.test/unit.js'; | ||
|
||
await Payments.payments('sq0idp-...', '', { scriptSrc: testSrc }); | ||
|
||
expect(mockLoadSquare).toHaveBeenCalledWith(testSrc); | ||
}); | ||
|
||
it('can resolve null', async () => { | ||
mockLoadSquare.mockResolvedValueOnce(null); | ||
|
||
const maybePayments = await Payments.payments('sq0idp-...'); | ||
expect(maybePayments).toBeNull(); | ||
}); | ||
|
||
it('resolves window.Square', async () => { | ||
const expected = true; | ||
const SQish = { | ||
payments() { | ||
return expected; | ||
}, | ||
}; | ||
mockLoadSquare.mockResolvedValue((SQish as unknown) as Square); | ||
|
||
const actual = await Payments.payments('sandbox-sq0idb-...'); | ||
|
||
expect(actual).toBe(expected); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { loadSquare } from './load'; | ||
|
||
const Version = 'v0'; | ||
|
||
class InvalidApplicationIdError extends Error { | ||
constructor( | ||
message = "The Payment 'applicationId' option is not in the correct format." | ||
) { | ||
super(message); | ||
this.name = 'InvalidApplicationIdError'; | ||
Object.setPrototypeOf(this, InvalidApplicationIdError.prototype); | ||
} | ||
} | ||
|
||
function getSrcForApplicationId(applicationId: string): string { | ||
let src = ''; | ||
|
||
if (applicationId.startsWith('sq0idp-')) { | ||
src = 'https://websdk.squarecdn.com/'; | ||
} | ||
|
||
if (applicationId.startsWith('sandbox-sq0idb-')) { | ||
src = 'https://sandbox.websdk.squarecdn.com/'; | ||
} | ||
|
||
if (src.length === 0) { | ||
throw new InvalidApplicationIdError(); | ||
} | ||
src += `${Version}/square.js`; | ||
|
||
return src; | ||
} | ||
|
||
export async function payments( | ||
applicationId: string, | ||
locationId?: string, | ||
overrides?: { | ||
scriptSrc?: string; | ||
} | ||
): Promise<Payments | null> { | ||
const src = | ||
overrides?.scriptSrc !== undefined | ||
? overrides.scriptSrc | ||
: getSrcForApplicationId(applicationId); | ||
|
||
const maybeSquare = await loadSquare(src); | ||
|
||
if (maybeSquare === null) { | ||
return null; | ||
} | ||
|
||
return maybeSquare.payments(applicationId, locationId); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
interface Payments { | ||
verifyBuyer: () => void; | ||
} | ||
|
||
interface Square { | ||
payments: (applicationId: string, locationId?: string) => Promise<Payments>; | ||
} | ||
|
||
interface Window { | ||
Square?: Square; | ||
} |