diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml new file mode 100644 index 00000000..32c4cbe0 --- /dev/null +++ b/.github/workflows/github-pages.yml @@ -0,0 +1,48 @@ +name: Deploy static content to Pages + +on: + push: + branches: [ "master" ] + + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + cache: "npm" + - name: Install dependencies + run: npm ci + - name: Build docs + run: | + npm run lint + npm run build + npm run test + npm run docs + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: './docs' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 00000000..51fb0ca3 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,37 @@ +name: Publish Package to npmjs + +on: + push: + tags: '*' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + cache: "npm" + - name: Run tests + run: | + npm run lint + npm run build + npm run test + + publish-npm: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + cache: "npm" + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run build + - run: npm publish --access=public + env: + NODE_AUTH_TOKEN: ${{secrets.NPMJS_AUTHTOKEN}} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..838562e2 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,34 @@ +name: Test Pull Request + +on: + pull_request: + types: + - opened + - reopened + - synchronize + push: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + name: Run js tests + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version-file: ".nvmrc" + cache: "npm" + - name: Install dependencies + run: npm ci + - name: Run tests + run: | + npm run lint + npm run build + npm run docs + - name: Run code coverage test + run: npm run cover + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..eb800ed4 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v18.19.0 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9777b1fa..00000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -dist: focal -language: node_js -node_js: '18' -cache: npm -before_install: - - npm config set @schibsted:registry=https://registry.npmjs.org/ - - npm config set //registry.npmjs.org/:_authToken=${NPMJS_AUTHTOKEN} -install: npm ci -script: - - npm run cover - - npm run build - - npm run docs -deploy: - - provider: script - skip_cleanup: true - script: npm publish --access=public - on: - branch: master - tags: true - - provider: pages - skip-cleanup: true - github-token: $GITHUB_TOKEN - keep-history: true - local-dir: docs - on: - branch: master diff --git a/README.md b/README.md index ce2bda33..620ebba8 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ Tags are pushed to NPM via Travis. To release a new version, run in master $ npm version ``` -which will run the test, update version in package.json, commi, tag the commit +which will run the test, update version in package.json, commit, tag the commit and push. ## LICENSE diff --git a/__tests__/identity.js b/__tests__/identity.js index 1eff8e64..e23c2279 100644 --- a/__tests__/identity.js +++ b/__tests__/identity.js @@ -305,32 +305,63 @@ describe('Identity', () => { describe('hasSession', () => { let identity; + const getSessionMock = jest.fn(() => ({ ok: true, json: () => Fixtures.sessionResponse })); + const mockSessionOkResponse = (response)=>{ + getSessionMock.mockImplementationOnce(() => ({ ok: true, json: () => response })); + } + beforeEach(() => { identity = new Identity(defaultOptions); - identity._sessionService.fetch = jest.fn(() => ({ ok: true, json: () => Fixtures.sessionResponse })); + identity._sessionService.fetch = getSessionMock; + identity._clearVarnishCookie(); + }); + + afterEach(()=>{ + jest.clearAllMocks(); + }) + + test('should clear varnish cookie for domain', async () => { + identity.enableVarnishCookie(10); + + mockSessionOkResponse({ result: true, sp_id: 'abc', baseDomain: 'spid.no' }); + + await identity.hasSession(); + + expect(document.cookie).toBe('SP_ID=abc'); + identity._clearVarnishCookie(); + + expect(document.cookie).toBe(''); }); test('should be able to set varnish cookie', async () => { await identity.hasSession(); + expect(document.cookie).toBe(''); + identity.enableVarnishCookie(); + await identity.hasSession(); expect(document.cookie).toBe('SP_ID=some-jwt-token'); }); test('should not set varnish cookie if session has no `expiresIn`', async () => { identity.enableVarnishCookie(); - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => ({ result: true, sp_id: 'abc' }) })); + + mockSessionOkResponse({ result: true, sp_id: 'abc' }); + await identity.hasSession(); + expect(document.cookie).toBe(''); }); test('should set varnish cookie also when reading from cache', async () => { identity.enableVarnishCookie(); - const session = { result: true, sp_id: 'should_not_expire', expiresIn: 2 }; - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => session })); + + mockSessionOkResponse({ result: true, sp_id: 'should_not_expire', expiresIn: 2 }) + await identity.hasSession(); + expect(document.cookie).toBe('SP_ID=should_not_expire'); // 1. Here we first wait a little bit (*less* than the 2 second cache expiry) @@ -349,95 +380,145 @@ describe('Identity', () => { test('should work to set varnish cache expiration', async () => { identity.enableVarnishCookie(3); - const session = { result: true, sp_id: 'should_remain_after_one_sec', expiresIn: 1 }; - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => session })); + + mockSessionOkResponse({ result: true, sp_id: 'should_remain_after_one_sec', expiresIn: 1 }) + await identity.hasSession(); + await new Promise((resolve) => setTimeout(resolve, 1010)); + expect(document.cookie).toBe('SP_ID=should_remain_after_one_sec'); }); test('should work to clear varnish cookie', async () => { + mockSessionOkResponse({ result: true, sp_id: 'should_be_cleared', expiresIn: 1 }); + identity.enableVarnishCookie(3); - const session = { result: true, sp_id: 'should_be_cleared', expiresIn: 1 }; - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => session })); + await identity.hasSession(); + expect(document.cookie).toBe('SP_ID=should_be_cleared'); + identity._maybeClearVarnishCookie(); + expect(document.cookie).toBe(''); }); describe('`baseDomain`', () => { test('should respect `baseDomain` from session', async () => { identity.enableVarnishCookie(); - const session1 = { result: true, sp_id: 'abc', expiresIn: 3600, baseDomain: 'foo.com' }; - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => session1 })); + + mockSessionOkResponse({ result: true, sp_id: 'abc', expiresIn: 3600, baseDomain: 'foo.com' }); + await identity.hasSession(); + expect(document.cookie).toBe(''); }); test('should respect `baseDomain` from session', async () => { + mockSessionOkResponse({ result: true, sp_id: 'abc', expiresIn: 3600 }); + identity.enableVarnishCookie(); - const session2 = { result: true, sp_id: 'abc', expiresIn: 3600 }; - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => session2 })); + await identity.hasSession(); + expect(document.cookie).toBe('SP_ID=abc'); }); }); describe(`enableVarnishCookie domain`, () => { - test('works', async () => { - identity.enableVarnishCookie({ domain: 'spid.no' }); - expect(identity.varnishCookieDomain).toBe('spid.no'); - const session1 = { result: true, sp_id: 'abc', expiresIn: 3600, baseDomain: 'tv.spid.no' }; - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => session1 })); - await identity.hasSession(); - expect(document.cookie).toBe('SP_ID=abc'); - }); + const domain = 'spid.no'; + const expiresIn= 10; + + beforeEach(()=>{ + //session base domain is `tv.spid.no` which is different from jest testURL, so cookie is not set + mockSessionOkResponse({ result: true, sp_id: 'abc', expiresIn: 3600, baseDomain: 'tv.spid.no' }); + }) + + const cases = [ + [undefined, undefined, 0, ''], + [{expiresIn}, undefined, expiresIn, ''], + [{domain}, domain, 0, 'SP_ID=abc'], + [{domain, expiresIn}, domain, expiresIn, 'SP_ID=abc'], + ] + + test.each(cases)( + "with %p as cookieSetup, %p as varnishCookieDomain, %p as varnishExpiresIn set cookies %p", + async (cookieConfig, varnishCookieDomain, varnishExpiresIn, exepectedCookie) => { + identity.enableVarnishCookie(cookieConfig); + + expect(identity.varnishCookieDomain).toBe(varnishCookieDomain); + expect(identity.varnishExpiresIn).toBe(varnishExpiresIn); + + await identity.hasSession(); + + expect(document.cookie).toBe(exepectedCookie); + } + ); }); test('should only go to session-service for site specific logout', async () => { - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: false, status: 400, statusText: 'No cookie present' })); + getSessionMock.mockImplementationOnce(() => ({ ok: false, status: 400, statusText: 'No cookie present' })); + await expect(identity.hasSession()).rejects.toMatchObject({ message: 'HasSession failed' }); + expect(identity._sessionService.fetch.mock.calls.length).toBe(1); expect(identity._sessionService.fetch.mock.calls[0][0]).toMatch(/^http:\/\/id.foo.com\/session/); }); test('should fail `hasSession` if session cookie is present but no session is found and site does not have specific logout', async () => { - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: false, status: 404, statusText: 'No session found' })); + getSessionMock.mockImplementationOnce(() => ({ ok: false, status: 404, statusText: 'No session found' })); + await expect(identity.hasSession()).rejects.toMatchObject({ message: 'HasSession failed' }); + expect(identity._sessionService.fetch.mock.calls.length).toBe(1); expect(identity._sessionService.fetch.mock.calls[0][0]).toMatch(/^http:\/\/id.foo.com\/session/); }); test('should terminate "chain" if session-service call succeeds', async () => { - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: true, json: () => ({}) })); + mockSessionOkResponse({}); + await expect(identity.hasSession()).resolves.toMatchObject({}); + expect(identity._sessionService.fetch.mock.calls.length).toBe(1); expect(identity._sessionService.fetch.mock.calls[0][0]).toMatch(/^http:\/\/id.foo.com\/session/); }); + test('should throw en SDK error when get /session returned an error', async () => { + mockSessionOkResponse({error: 'some error'}); + + await expect(identity.hasSession()).rejects.toThrowError('HasSession failed'); + }); + test('should emit event both when "real" and "cached" values are used', async () => { const spy = jest.fn(); + identity.on('login', spy); + await identity.hasSession(); await identity.hasSession(); + expect(spy).toHaveBeenCalledTimes(2); }); test('should return the same promise if invoked multiple times', async () => { - identity._sessionService.fetch.mockImplementationOnce(() => new Promise((resolve) => { - setTimeout(resolve({ ok: true, json: () => ({ sp_id: 'yo' }) }), 1); - })); + mockSessionOkResponse({ sp_id: 'yo' }); + const promise1 = identity.hasSession(); const promise2 = identity.hasSession(); // NOTE: no 'await' — we want the promise + expect(promise2).toBe(promise1); + const dummy = await promise1; + expect(dummy).toMatchObject({ sp_id: 'yo' }); }); test('should throw error if session-service returns error without 404', async () => { - identity._sessionService.fetch.mockImplementationOnce(() => ({ ok: false, status: 401, statusText: 'Unauthorized' })); + getSessionMock.mockImplementationOnce(() => ({ ok: false, status: 401, statusText: 'Unauthorized' })); + await expect(identity.hasSession()).rejects.toMatchObject({ message: 'HasSession failed' }); + expect(identity._sessionService.fetch.mock.calls.length).toBe(1); expect(identity._sessionService.fetch.mock.calls[0][0]).toMatch(/^http:\/\/id.foo.com\/session/); }); @@ -445,6 +526,7 @@ describe('Identity', () => { describe('cache', () => { test('should never cache if caching is off', async () => { identity._enableSessionCaching = false; + await identity.hasSession(); await identity.hasSession(); @@ -459,18 +541,25 @@ describe('Identity', () => { }); test('cache shouldn\'t be updated when hasSession returns data from cache, but should be if cache expired', async () => { - const getExpiresOn = () => JSON.parse(identity.cache.cache.get('hasSession-cache')).expiresOn; jest.spyOn(Date, 'now') .mockReturnValue(new Date("2019-11-09T10:00:00").getTime()); + + const getExpiresOn = () => JSON.parse(identity.cache.cache.get('hasSession-cache')).expiresOn; + await identity.hasSession(); + const cacheExpires = getExpiresOn(); jest.spyOn(Date, 'now') .mockReturnValue(new Date("2019-11-09T10:02:00").getTime()); + await identity.hasSession(); + expect(getExpiresOn()).toBe(cacheExpires); // expiresOn shouldn't change on call less than 5m jest.spyOn(Date, 'now') .mockReturnValue(new Date("2019-11-09T11:05:00").getTime()); + await identity.hasSession(); + // expiresOn should change after 1h expect(getExpiresOn()).not.toBe(cacheExpires); }); diff --git a/__tests__/object.utest.js b/__tests__/object.utest.js index 4d1c2f88..2e87e03e 100644 --- a/__tests__/object.utest.js +++ b/__tests__/object.utest.js @@ -70,25 +70,28 @@ describe('object', () => { expect(cloneDefined({foo: 1, bar: 2}, {foo: 2})).toEqual({foo: 2, bar: 2}); }); - test('deeply clones any objectvalue', () => { + test('deeply clones any object with values', () => { const obj = {a: 'foo'}; const result = cloneDefined({ a: 'bar' }, obj); expect(result).toEqual({a: 'foo'}); expect(result).not.toBe(obj); }); - test('deeply clones any objectvalue', () => { + test('deeply clones any object value with values', () => { const arr = [13]; const result = cloneDefined({ a: 'bar' }, arr); expect(result).toEqual({a: 'bar', '0': 13}); expect(result).not.toBe(arr); - }); + test('deeply clones nested object with values', () => { + const obj = {a: 'foo', b: {c: 1, d: 2}}; + const result = cloneDefined({a: 'bar', b: {c: 1}}, obj); + expect(result).toEqual({a: 'foo', b: {c: 1, d: 2}}); + }); }); describe('cloneDeep()', () => { - test('clones an empty object', () => { const a = {}; const b = cloneDeep(a); diff --git a/package-lock.json b/package-lock.json index 8a8aa1a0..e517c7ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@babel/core": "^7.11.4", "@babel/preset-env": "^7.23.2", "babel-loader": "^8.1.0", - "codecov": "^3.6.5", "core-js": "^3.6.5", "docdash": "git+https://github.com/torarvid/docdash.git#v0.5.0", "eslint": "^6.8.0", @@ -2489,15 +2488,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/babel__core": { "version": "7.1.9", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", @@ -2881,18 +2871,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", - "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { "version": "6.12.4", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", @@ -2987,15 +2965,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", - "dev": true, - "engines": { - "node": ">=0.6.10" - } - }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -3997,25 +3966,6 @@ "node": ">= 0.12.0" } }, - "node_modules/codecov": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.7.2.tgz", - "integrity": "sha512-fmCjAkTese29DUX3GMIi4EaKGflHa4K51EoMc29g8fBHawdk/+KEq5CWOeXLdd9+AT7o1wO4DIpp/Z1KCqCz1g==", - "dev": true, - "dependencies": { - "argv": "0.0.2", - "ignore-walk": "3.0.3", - "js-yaml": "3.13.1", - "teeny-request": "6.0.1", - "urlgrey": "0.4.4" - }, - "bin": { - "codecov": "bin/codecov" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -6145,20 +6095,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -6180,28 +6116,6 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "dependencies": { - "agent-base": "5", - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/https-proxy-agent/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/human-signals": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", @@ -6244,15 +6158,6 @@ "node": ">= 4" } }, - "node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4" - } - }, "node_modules/import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -11477,15 +11382,6 @@ "stream-shift": "^1.0.0" } }, - "node_modules/stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "dev": true, - "dependencies": { - "stubs": "^3.0.0" - } - }, "node_modules/stream-http": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", @@ -11642,12 +11538,6 @@ "node": ">=8" } }, - "node_modules/stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", - "dev": true - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -11759,19 +11649,6 @@ "node": ">=6" } }, - "node_modules/teeny-request": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.1.tgz", - "integrity": "sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g==", - "dev": true, - "dependencies": { - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^4.0.0", - "node-fetch": "^2.2.0", - "stream-events": "^1.0.5", - "uuid": "^3.3.2" - } - }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -12336,12 +12213,6 @@ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "dev": true }, - "node_modules/urlgrey": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", - "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=", - "dev": true - }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -15255,12 +15126,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, "@types/babel__core": { "version": "7.1.9", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", @@ -15635,15 +15500,6 @@ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, - "agent-base": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", - "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", - "dev": true, - "requires": { - "debug": "4" - } - }, "ajv": { "version": "6.12.4", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", @@ -15725,12 +15581,6 @@ "sprintf-js": "~1.0.2" } }, - "argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", - "dev": true - }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -16565,19 +16415,6 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "codecov": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.7.2.tgz", - "integrity": "sha512-fmCjAkTese29DUX3GMIi4EaKGflHa4K51EoMc29g8fBHawdk/+KEq5CWOeXLdd9+AT7o1wO4DIpp/Z1KCqCz1g==", - "dev": true, - "requires": { - "argv": "0.0.2", - "ignore-walk": "3.0.3", - "js-yaml": "3.13.1", - "teeny-request": "6.0.1", - "urlgrey": "0.4.4" - } - }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -18362,17 +18199,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -18390,24 +18216,6 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "requires": { - "agent-base": "5", - "debug": "4" - }, - "dependencies": { - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true - } - } - }, "human-signals": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", @@ -18441,15 +18249,6 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -22719,15 +22518,6 @@ "stream-shift": "^1.0.0" } }, - "stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "dev": true, - "requires": { - "stubs": "^3.0.0" - } - }, "stream-http": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", @@ -22860,12 +22650,6 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, - "stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -22957,19 +22741,6 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, - "teeny-request": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.1.tgz", - "integrity": "sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g==", - "dev": true, - "requires": { - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^4.0.0", - "node-fetch": "^2.2.0", - "stream-events": "^1.0.5", - "uuid": "^3.3.2" - } - }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -23428,12 +23199,6 @@ } } }, - "urlgrey": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", - "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=", - "dev": true - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", diff --git a/package.json b/package.json index a58e031d..0e36ac77 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,10 @@ "clean": "rimraf .cache coverage dist docs", "docs": "rimraf docs && jsdoc -c ./jsdoc.conf.json --verbose", "lint": "eslint .", - "pretest": "npm run lint", + "lint:fix": "eslint . --fix", "test": "jest", - "precover": "npm run lint", "cover": "jest --coverage", - "postcover": "codecov", - "preversion": "npm test", + "preversion": "npm run lint && npm test", "version": "node ./scripts/genversion.js && git add src/version.js", "postversion": "git push && git push --tags" }, @@ -27,7 +25,6 @@ "@babel/core": "^7.11.4", "@babel/preset-env": "^7.23.2", "babel-loader": "^8.1.0", - "codecov": "^3.6.5", "core-js": "^3.6.5", "docdash": "git+https://github.com/torarvid/docdash.git#v0.5.0", "eslint": "^6.8.0", diff --git a/src/identity.d.ts b/src/identity.d.ts index ffd26302..b6c9c4d7 100644 --- a/src/identity.d.ts +++ b/src/identity.d.ts @@ -236,6 +236,8 @@ export class Identity extends TinyEmitter { * * @description This function calls {@link Identity#hasSession} internally and thus has the side * effect that it might perform an auto-login on the user + * @param {string} externalParty + * @param {string|null} optionalSuffix * @throws {SDKError} If the `pairId` is missing in user session. * @throws {SDKError} If the `externalParty` is not defined * @return {Promise} The merchant- and 3rd-party-specific `externalId` diff --git a/src/identity.js b/src/identity.js index eae4d37b..1041c20f 100644 --- a/src/identity.js +++ b/src/identity.js @@ -365,7 +365,8 @@ export class Identity extends EventEmitter { expiresIn = options; } else if (typeof options == 'object') { - ({ expiresIn = expiresIn, domain = domain } = options); + expiresIn = options.expiresIn || expiresIn; + domain = options.domain || domain; } assert(Number.isInteger(expiresIn), `'expiresIn' must be an integer`); @@ -428,10 +429,12 @@ export class Identity extends EventEmitter { * @returns {void} */ _clearVarnishCookie() { + const baseDomain = this._session && typeof this._session.baseDomain === 'string' + ? this._session.baseDomain + : document.domain; + let domain = this.varnishCookieDomain || - ((this._session && typeof this._session.baseDomain === 'string') - ? this._session.baseDomain - : document.domain) || + baseDomain || ''; document.cookie = `SP_ID=nothing; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.${domain}`; @@ -631,6 +634,8 @@ export class Identity extends EventEmitter { * * @description This function calls {@link Identity#hasSession} internally and thus has the side * effect that it might perform an auto-login on the user + * @param {string} externalParty + * @param {string|null} optionalSuffix * @throws {SDKError} If the `pairId` is missing in user session. * @throws {SDKError} If the `externalParty` is not defined * @return {Promise} The merchant- and 3rd-party-specific `externalId` diff --git a/src/object.js b/src/object.js index 254ccbc4..c23a95b3 100644 --- a/src/object.js +++ b/src/object.js @@ -28,21 +28,21 @@ import SDKError from './SDKError.js'; * keys for undefined values are removed. */ export function cloneDefined(...sources) { - const dest = {}; + const result = {}; if (!(sources && sources.length)) { throw new SDKError('No objects to clone'); } - sources.forEach(src => { - assert(isObject(src)); - if (isNonEmptyObj(src)) { - Object.entries(src).forEach(([key, val]) => { - if (val !== undefined ) { // eslint-disable-line no-undefined - dest[key] = isObject(val) ? cloneDeep(val) : val; + sources.forEach(source => { + assert(isObject(source)); + if (isNonEmptyObj(source)) { + Object.entries(source).forEach(([key, value]) => { + if (typeof value !== 'undefined' ) { + result[key] = isObject(value) ? cloneDeep(value) : value; } }); } }); - return dest; + return result; } /**