From 0e553700d98df2de38eefacf50189b33265b38d9 Mon Sep 17 00:00:00 2001 From: Westin Wrzesinski Date: Tue, 28 Jun 2022 21:30:49 -0500 Subject: [PATCH 1/4] chore!: convert library to typescript - Codebase is migrated to TypeScript by using https://github.com/Khan/flow-to-ts - Dist folder now outputs esm folder - Package.json update for types & modules - Flowgen is used to generate flow types from dist/*.d.ts --- .eslintrc.js | 8 +- .github/workflows/main.yml | 3 - README.md | 18 +- babel.config.js | 4 + babel.config.json | 3 - karma.conf.js | 14 -- package.json | 37 ++-- src/constants.js | 14 -- src/constants.ts | 12 ++ src/{index.js => index.ts} | 2 - src/types.js | 42 ---- src/types.ts | 12 ++ src/util.js | 11 - src/util.ts | 8 + src/{utils.js => utils.ts} | 299 +++++++++++++++------------ test/closeWindow.test.ts | 19 ++ test/getActualDomain.test.ts | 21 ++ test/getAllFramesInWindow.test.ts | 65 ++++++ test/getDomain.test.ts | 136 ++++++++++++ test/getDomainFromUrl.test.ts | 33 +++ test/getOpener.test.ts | 43 ++++ test/getParent.test.ts | 36 ++++ test/getParents.test.ts | 36 ++++ test/getUserAgent.test.ts | 37 ++++ test/index.js | 5 - test/isBlankDomain.test.ts | 53 +++++ test/isFileProtocol.test.ts | 33 +++ test/isSameDomain.test.ts | 212 +++++++++++++++++++ test/matchDomain.test.ts | 19 ++ test/stringifyDomainPattern.test.ts | 28 +++ test/tests/closeWindow.js | 22 -- test/tests/getActualDomain.js | 24 --- test/tests/getAllFramesInWindow.js | 63 ------ test/tests/getDomain.js | 153 -------------- test/tests/getDomainFromUrl.js | 43 ---- test/tests/getOpener.js | 56 ----- test/tests/getParent.js | 46 ----- test/tests/getParents.js | 42 ---- test/tests/getUserAgent.js | 44 ---- test/tests/index.js | 16 -- test/tests/isBlankDomain.js | 57 ----- test/tests/isFileProtocol.js | 38 ---- test/tests/isSameDomain.js | 241 --------------------- test/tests/matchDomain.js | 25 --- test/tests/stringifyDomainPattern.js | 35 ---- test/util.js | 11 - test/utils.ts | 18 ++ test/win.js | 15 -- tsconfig.json | 3 + vite.config.js | 31 +++ webpack.config.ts | 39 ++++ 51 files changed, 1092 insertions(+), 1193 deletions(-) create mode 100644 babel.config.js delete mode 100644 babel.config.json delete mode 100644 karma.conf.js delete mode 100644 src/constants.js create mode 100644 src/constants.ts rename src/{index.js => index.ts} (85%) delete mode 100644 src/types.js create mode 100644 src/types.ts delete mode 100644 src/util.js create mode 100644 src/util.ts rename src/{utils.js => utils.ts} (72%) create mode 100644 test/closeWindow.test.ts create mode 100644 test/getActualDomain.test.ts create mode 100644 test/getAllFramesInWindow.test.ts create mode 100644 test/getDomain.test.ts create mode 100644 test/getDomainFromUrl.test.ts create mode 100644 test/getOpener.test.ts create mode 100644 test/getParent.test.ts create mode 100644 test/getParents.test.ts create mode 100644 test/getUserAgent.test.ts delete mode 100644 test/index.js create mode 100644 test/isBlankDomain.test.ts create mode 100644 test/isFileProtocol.test.ts create mode 100644 test/isSameDomain.test.ts create mode 100644 test/matchDomain.test.ts create mode 100644 test/stringifyDomainPattern.test.ts delete mode 100644 test/tests/closeWindow.js delete mode 100644 test/tests/getActualDomain.js delete mode 100644 test/tests/getAllFramesInWindow.js delete mode 100644 test/tests/getDomain.js delete mode 100644 test/tests/getDomainFromUrl.js delete mode 100644 test/tests/getOpener.js delete mode 100644 test/tests/getParent.js delete mode 100644 test/tests/getParents.js delete mode 100644 test/tests/getUserAgent.js delete mode 100644 test/tests/index.js delete mode 100644 test/tests/isBlankDomain.js delete mode 100644 test/tests/isFileProtocol.js delete mode 100644 test/tests/isSameDomain.js delete mode 100644 test/tests/matchDomain.js delete mode 100644 test/tests/stringifyDomainPattern.js delete mode 100644 test/util.js create mode 100644 test/utils.ts delete mode 100644 test/win.js create mode 100644 tsconfig.json create mode 100644 vite.config.js create mode 100644 webpack.config.ts diff --git a/.eslintrc.js b/.eslintrc.js index a4e00f0..8a8b1ce 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,9 +1,11 @@ -/* @flow */ - module.exports = { - extends: "./node_modules/@krakenjs/eslint-config-grumbler", + extends: + "./node_modules/@krakenjs/eslint-config-grumbler/eslintrc-typescript", globals: { __TEST__: true, }, + + rules: { + }, }; diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 80619fc..527d944 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,9 +27,6 @@ jobs: - name: 👕 Lint commit messages uses: wagoid/commitlint-github-action@v4 - - name: ▶️ Run flow-typed script - run: npm run flow-typed - - name: ▶️ Run build script run: npm run build diff --git a/README.md b/README.md index c3d63c4..6f4a27d 100644 --- a/README.md +++ b/README.md @@ -207,17 +207,7 @@ Returns true if the specified domain matches the pattern. The pattern can be one All of the tasks are listed in the package.json file under the scripts section -| Command | Description | -| ------------- | :--------------------------------------: | -| npm run build | Builds the dist files | -| npm test | Runs the test suite. Lint + Type + Karma | - -# Debugging - -Run the debug task and pass the next tasks as argument. - -``` -npm run debug -- npm run build -npm run debug -- npm test -npm run debug -- npm run karma -- --browsers=Chrome -``` +| Command | Description | +| ------------- | :-------------------: | +| npm run build | Builds the dist files | +| npm test | Runs full test suite. | diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..dd684c7 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + extends: "@krakenjs/babel-config-grumbler/babelrc-browser", + presets: ["@krakenjs/babel-config-grumbler/flow-ts-babel-preset"], +}; diff --git a/babel.config.json b/babel.config.json deleted file mode 100644 index 5f06501..0000000 --- a/babel.config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@krakenjs/babel-config-grumbler/babelrc-browser" -} diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index 6b7b2ce..0000000 --- a/karma.conf.js +++ /dev/null @@ -1,14 +0,0 @@ -/* @flow */ - -import { getKarmaConfig } from "@krakenjs/karma-config-grumbler"; - -import { WEBPACK_CONFIG_TEST } from "./webpack.config"; - -export default function configKarma(karma: Object) { - let karmaConfig = getKarmaConfig(karma, { - basePath: __dirname, - webpack: WEBPACK_CONFIG_TEST, - }); - - karma.set(karmaConfig); -} diff --git a/package.json b/package.json index 60f9ef5..f10c0e8 100644 --- a/package.json +++ b/package.json @@ -3,25 +3,29 @@ "version": "3.0.2", "description": "Utilities for dealing with cross-domain windows.", "main": "dist/cross-domain-utils.js", - "module": "dist/module/index.js", + "module": "dist/index.js", + "types": "dist/types/index.d.ts", + "sideEffects": false, "scripts": { - "build": "npm run test && npm run babel && npm run webpack", + "build": "npm run test && npm run babel && npm run webpack && npm run build:types", + "build:flow": "find ./dist -type f -not -path './node_modules/*' -name '*.d.ts' -exec sh -c 'flowgen --add-flow-header $1 -o ${1%.*.*}.js.flow' _ '{}' \\;", + "build:tsc": "tsc src/* --outDir ./dist/esm --declaration --emitDeclarationOnly", + "build:types": "npm run build:tsc && npm run build:flow", "webpack": "cross-env NODE_ENV=production babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --progress --output-path dist", - "babel": "cross-env NODE_ENV=production babel src/ --out-dir dist/module", - "karma": "cross-env NODE_ENV=test babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/karma start", + "babel": "cross-env NODE_ENV=production babel src/ --out-dir ./dist/esm/ --extensions .ts,.tsx", + "tsc": "tsc", "format": "prettier --write --ignore-unknown .", "format:check": "prettier --check .", - "test": "npm run format:check && npm run lint && npm run flow-typed && npm run flow && npm run karma", - "lint": "eslint src/ test/", - "flow": "flow", - "flow-typed": "rm -rf ./flow-typed && flow-typed install", + "test": "npm run format:check && npm run lint && npm run tsc --no-emit && npm run vitest", + "lint": "eslint --ext ts,tsx,js,jsx src/ test/", "clean": "rimraf dist coverage", - "reinstall": "rimraf flow-typed && rimraf node_modules && npm install && flow-typed install", "prerelease": "npm run clean && npm run build && git add dist && git commit -m 'ci: check in dist folder' || echo 'Nothing to distribute'", "release": "standard-version", "postrelease": "git push && git push --follow-tags && npm publish", "debug": "cross-env NODE_ENV=debug", - "prepare": "husky install" + "prepare": "husky install", + "vitest": "vitest run --dom --coverage", + "vitest:watch": "vitest watch --dom --coverage --ui" }, "repository": { "type": "git", @@ -72,7 +76,6 @@ ], "license": "Apache-2.0", "files": [ - "src/", "dist/" ], "readmeFilename": "README.md", @@ -80,15 +83,19 @@ "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", "@krakenjs/grumbler-scripts": "^8.0.5", + "@vitest/ui": "^0.16.0", + "c8": "^7.11.0", "chai": "^4.2.0", "cross-env": "^7.0.3", - "flow-bin": "0.155.0", - "flow-typed": "^3.8.0", + "flowgen": "^1.16.0", + "happy-dom": "^2.55.0", "husky": "^7.0.4", "lint-staged": "^13.0.3", - "mocha": "^4", "prettier": "2.7.1", - "standard-version": "^9.3.2" + "standard-version": "^9.3.2", + "ts-node": "^10.5.0", + "typescript": "4.6.3", + "vitest": "0.16.0" }, "lint-staged": { "**/*": "prettier --write --ignore-unknown" diff --git a/src/constants.js b/src/constants.js deleted file mode 100644 index 2975240..0000000 --- a/src/constants.js +++ /dev/null @@ -1,14 +0,0 @@ -/* @flow */ - -export const PROTOCOL = { - MOCK: ("mock:": "mock:"), - FILE: ("file:": "file:"), - ABOUT: ("about:": "about:"), -}; - -export const WILDCARD = "*"; - -export const WINDOW_TYPE = { - IFRAME: ("iframe": "iframe"), - POPUP: ("popup": "popup"), -}; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..d79d85e --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,12 @@ +export const PROTOCOL = { + MOCK: "mock:" as const, + FILE: "file:" as const, + ABOUT: "about:" as const, +}; + +export const WILDCARD = "*"; + +export const WINDOW_TYPE = { + IFRAME: "iframe" as const, + POPUP: "popup" as const, +}; diff --git a/src/index.js b/src/index.ts similarity index 85% rename from src/index.js rename to src/index.ts index 9b8590f..0cee50e 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,5 +1,3 @@ -/* @flow */ - export * from "./utils"; export * from "./types"; export * from "./constants"; diff --git a/src/types.js b/src/types.js deleted file mode 100644 index e911ddc..0000000 --- a/src/types.js +++ /dev/null @@ -1,42 +0,0 @@ -/* @flow */ - -// export something to force webpack to see this as an ES module -export const TYPES = true; - -export type CrossDomainLocationType = {||}; - -export type CrossDomainWindowType = {| - location: string | CrossDomainLocationType, - self: CrossDomainWindowType, - closed: boolean, - open: (string, string, string) => CrossDomainWindowType, - close: () => void, - focus: () => void, - top: CrossDomainWindowType, - frames: $ReadOnlyArray, - opener?: CrossDomainWindowType, - parent: CrossDomainWindowType, - length: number, - postMessage: (string, string) => void, -|}; - -export type SameDomainWindowType = Object & {| - location: string | Object, - self: CrossDomainWindowType, - closed: boolean, - open: (string, string, string) => CrossDomainWindowType, - close: () => void, - focus: () => void, - XMLHttpRequest: typeof XMLHttpRequest, - document: Document, - navigator: {| - userAgent: string, - mockUserAgent?: string, - |}, -|}; - -export type DomainMatcher = - | string - | $ReadOnlyArray - | $ReadOnlyArray - | RegExp; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..92ae4e0 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,12 @@ +export type CrossDomainWindowType = Window; + +export type SameDomainWindowType = Omit< + Window, + "frames" | "parent" | "focus" | "top" | "opener" | "postMessage" +>; + +export type DomainMatcher = + | string + | ReadonlyArray + | ReadonlyArray + | RegExp; diff --git a/src/util.js b/src/util.js deleted file mode 100644 index 7d2d48f..0000000 --- a/src/util.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -export function isRegex(item: mixed): boolean { - // $FlowFixMe method-unbinding - return Object.prototype.toString.call(item) === "[object RegExp]"; -} - -// eslint-disable-next-line no-unused-vars -export function noop(...args: $ReadOnlyArray) { - // pass -} diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..f3acde5 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,8 @@ +export function isRegex(item: unknown): boolean { + return Object.prototype.toString.call(item) === "[object RegExp]"; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function noop(...args: ReadonlyArray): void { + // pass +} diff --git a/src/utils.js b/src/utils.ts similarity index 72% rename from src/utils.js rename to src/utils.ts index f8673c5..e18a7ef 100644 --- a/src/utils.js +++ b/src/utils.ts @@ -1,6 +1,5 @@ -/* @flow */ -/* eslint max-lines: 0 */ - +/* eslint max-lines: 0, @typescript-eslint/prefer-optional-chain: 0 */ +/* global NodeJS */ import { isRegex, noop } from "./util"; import type { CrossDomainWindowType, @@ -11,15 +10,19 @@ import { PROTOCOL, WILDCARD } from "./constants"; const IE_WIN_ACCESS_ERROR = "Call was rejected by callee.\r\n"; -export function getActualProtocol(win: SameDomainWindowType = window): ?string { +export function getActualProtocol(win: SameDomainWindowType = window): string { return win.location.protocol; } -export function getProtocol(win: SameDomainWindowType = window): ?string { +export function getProtocol(win: SameDomainWindowType = window): string { + // @ts-ignore mockDomain does not exist on window if (win.mockDomain) { + // @ts-ignore mockDomain does not exist on window + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call const protocol = win.mockDomain.split("//")[0]; if (protocol) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return protocol; } } @@ -40,8 +43,8 @@ export function isMockProtocol(win: SameDomainWindowType = window): boolean { } export function getParent( - win?: CrossDomainWindowType = window -): ?CrossDomainWindowType { + win: CrossDomainWindowType = window +): CrossDomainWindowType | null | undefined { if (!win) { return; } @@ -50,14 +53,14 @@ export function getParent( if (win.parent && win.parent !== win) { return win.parent; } - } catch (err) { + } catch (err: unknown) { // pass } } export function getOpener( - win?: CrossDomainWindowType = window -): ?CrossDomainWindowType { + win: CrossDomainWindowType = window +): CrossDomainWindowType | null | undefined { if (!win) { return; } @@ -68,8 +71,8 @@ export function getOpener( } try { - return win.opener; - } catch (err) { + return win.opener as CrossDomainWindowType; + } catch (err: unknown) { // pass } } @@ -78,17 +81,16 @@ export function canReadFromWindow( win: CrossDomainWindowType | SameDomainWindowType ): boolean { try { - // $FlowFixMe noop(win && win.location && win.location.href); return true; - } catch (err) { + } catch (err: unknown) { // pass } return false; } -export function getActualDomain(win?: SameDomainWindowType = window): string { +export function getActualDomain(win: SameDomainWindowType = window): string { const location = win.location; if (!location) { @@ -106,9 +108,11 @@ export function getActualDomain(win?: SameDomainWindowType = window): string { } if (protocol === PROTOCOL.ABOUT) { + // @ts-ignore - trying to reassing to something that might be window const parent = getParent(win); + if (parent && canReadFromWindow(parent)) { - // $FlowFixMe + // @ts-ignore return getActualDomain(parent); } @@ -124,11 +128,18 @@ export function getActualDomain(win?: SameDomainWindowType = window): string { return `${protocol}//${host}`; } -export function getDomain(win?: SameDomainWindowType = window): string { +export function getDomain(win: SameDomainWindowType = window): string { const domain = getActualDomain(win); - if (domain && win.mockDomain && win.mockDomain.indexOf(PROTOCOL.MOCK) === 0) { - return win.mockDomain; + if ( + domain && + // @ts-ignore - mockDomain + win.mockDomain && + // @ts-ignore - mockDomain + (win.mockDomain as string).indexOf(PROTOCOL.MOCK) === 0 + ) { + // @ts-ignore - mockDomain + return win.mockDomain as string; } return domain; @@ -136,7 +147,6 @@ export function getDomain(win?: SameDomainWindowType = window): string { export function isBlankDomain(win: CrossDomainWindowType): boolean { try { - // $FlowFixMe if (!win.location.href) { return true; } @@ -144,19 +154,21 @@ export function isBlankDomain(win: CrossDomainWindowType): boolean { if (win.location.href === "about:blank") { return true; } - } catch (err) { + } catch (err: unknown) { // pass } return false; } -export function isActuallySameDomain(win: CrossDomainWindowType): boolean { +export function isActuallySameDomain( + win: CrossDomainWindowType | SameDomainWindowType +): boolean { try { if (win === window) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -166,21 +178,19 @@ export function isActuallySameDomain(win: CrossDomainWindowType): boolean { if (desc && desc.enumerable === false) { return false; } - } catch (err) { + } catch (err: unknown) { // pass } try { - // $FlowFixMe if (isAboutProtocol(win) && canReadFromWindow(win)) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } try { - // $FlowFixMe if (isMockProtocol(win) && canReadFromWindow(win)) { return true; } @@ -189,11 +199,10 @@ export function isActuallySameDomain(win: CrossDomainWindowType): boolean { } try { - // $FlowFixMe if (getActualDomain(win) === getActualDomain(window)) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -212,16 +221,14 @@ export function isSameDomain( return true; } - // $FlowFixMe if (isAboutProtocol(win) && canReadFromWindow(win)) { return true; } - // $FlowFixMe if (getDomain(window) === getDomain(win)) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -235,13 +242,12 @@ export function assertSameDomain( throw new Error(`Expected window to be same domain`); } - // $FlowFixMe return win; } export function getParents( win: CrossDomainWindowType -): $ReadOnlyArray { +): ReadonlyArray { const result = []; try { @@ -249,7 +255,7 @@ export function getParents( result.push(win.parent); win = win.parent; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -279,14 +285,13 @@ export function isAncestorParent( export function getFrames( win: CrossDomainWindowType -): $ReadOnlyArray { - const result = []; - +): Array { + const result: Array = []; let frames; try { frames = win.frames; - } catch (err) { + } catch (err: unknown) { frames = win; } @@ -294,7 +299,7 @@ export function getFrames( try { len = frames.length; - } catch (err) { + } catch (err: unknown) { // pass } @@ -308,7 +313,7 @@ export function getFrames( try { frame = frames[i]; - } catch (err) { + } catch (err: unknown) { continue; } @@ -323,7 +328,7 @@ export function getFrames( try { frame = frames[i]; - } catch (err) { + } catch (err: unknown) { return result; } @@ -339,7 +344,7 @@ export function getFrames( export function getAllChildFrames( win: CrossDomainWindowType -): $ReadOnlyArray { +): ReadonlyArray { const result = []; for (const frame of getFrames(win)) { @@ -354,13 +359,13 @@ export function getAllChildFrames( } export function getTop( - win?: CrossDomainWindowType = window -): ?CrossDomainWindowType { + win: CrossDomainWindowType = window +): CrossDomainWindowType | null | undefined { try { if (win.top) { return win.top; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -372,7 +377,7 @@ export function getTop( if (isAncestorParent(window, win) && window.top) { return window.top; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -380,7 +385,7 @@ export function getTop( if (isAncestorParent(win, window) && window.top) { return window.top; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -389,7 +394,7 @@ export function getTop( if (frame.top) { return frame.top; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -400,14 +405,14 @@ export function getTop( } export function getNextOpener( - win?: CrossDomainWindowType = window -): ?CrossDomainWindowType { + win: CrossDomainWindowType = window +): CrossDomainWindowType | null | undefined { return getOpener(getTop(win) || win); } export function getUltimateTop( - win?: CrossDomainWindowType = window -): CrossDomainWindowType { + win: CrossDomainWindowType = window +): CrossDomainWindowType | null { const opener = getNextOpener(win); if (opener) { @@ -419,7 +424,7 @@ export function getUltimateTop( export function getAllFramesInWindow( win: CrossDomainWindowType -): $ReadOnlyArray { +): ReadonlyArray { const top = getTop(win); if (!top) { @@ -437,8 +442,8 @@ export function getAllFramesInWindow( } export function getAllWindows( - win?: CrossDomainWindowType = window -): $ReadOnlyArray { + win: CrossDomainWindowType = window +): ReadonlyArray { const frames = getAllFramesInWindow(win); const opener = getNextOpener(win); @@ -468,10 +473,12 @@ export function isFrameWindowClosed(frame: HTMLIFrameElement): boolean { let parent = frame; while (parent.parentNode && parent.parentNode !== parent) { + // @ts-ignore parent = parent.parentNode; } - // $FlowFixMe + // @ts-ignore - host does not exist on type frame + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument if (!parent.host || !doc.documentElement.contains(parent.host)) { return true; } @@ -480,13 +487,13 @@ export function isFrameWindowClosed(frame: HTMLIFrameElement): boolean { return false; } -function safeIndexOf(collection: $ReadOnlyArray, item: T): number { +function safeIndexOf(collection: ReadonlyArray, item: T): number { for (let i = 0; i < collection.length; i++) { try { if (collection[i] === item) { return i; } - } catch (err) { + } catch (err: unknown) { // pass } } @@ -494,18 +501,18 @@ function safeIndexOf(collection: $ReadOnlyArray, item: T): number { return -1; } -const iframeWindows = []; -const iframeFrames = []; +const iframeWindows: Array = []; +const iframeFrames: Array = []; export function isWindowClosed( win: CrossDomainWindowType, - allowMock: boolean = true + allowMock = true ): boolean { try { if (win === window) { return false; } - } catch (err) { + } catch (err: unknown) { return true; } @@ -513,7 +520,7 @@ export function isWindowClosed( if (!win) { return true; } - } catch (err) { + } catch (err: unknown) { return true; } @@ -521,10 +528,9 @@ export function isWindowClosed( if (win.closed) { return true; } - } catch (err) { + } catch (err: unknown) { // I love you so much IE - - if (err && err.message === IE_WIN_ACCESS_ERROR) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return false; } @@ -533,36 +539,33 @@ export function isWindowClosed( if (allowMock && isSameDomain(win)) { try { - // $FlowFixMe + // @ts-ignore if (win.mockclosed) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } } // Mobile safari - try { if (!win.parent || !win.top) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } // Yes, this actually happens in IE. win === win errors out when the window // is from an iframe, and the iframe was removed from the page. - try { noop(win === win); // eslint-disable-line no-self-compare - } catch (err) { + } catch (err: unknown) { return true; } // IE orphaned frame - const iframeIndex = safeIndexOf(iframeWindows, win); if (iframeIndex !== -1) { @@ -576,13 +579,13 @@ export function isWindowClosed( return false; } -function cleanIframes() { +function cleanIframes(): void { for (let i = 0; i < iframeWindows.length; i++) { let closed = false; try { closed = iframeWindows[i].closed; - } catch (err) { + } catch (err: unknown) { // pass } @@ -593,60 +596,66 @@ function cleanIframes() { } } -export function linkFrameWindow(frame: HTMLIFrameElement) { +export function linkFrameWindow(frame: HTMLIFrameElement): void { cleanIframes(); if (frame && frame.contentWindow) { try { iframeWindows.push(frame.contentWindow); iframeFrames.push(frame); - } catch (err) { + } catch (err: unknown) { // pass } } } -export function getUserAgent(win: ?SameDomainWindowType): string { +export function getUserAgent( + win: SameDomainWindowType | null | undefined +): string { win = win || window; - return win.navigator.mockUserAgent || win.navigator.userAgent; + // @ts-ignore + return (win.navigator.mockUserAgent as string) || win.navigator.userAgent; } export function getFrameByName( win: CrossDomainWindowType, name: string -): ?CrossDomainWindowType { +): CrossDomainWindowType | null | undefined { const winFrames = getFrames(win); for (const childFrame of winFrames) { try { if ( isSameDomain(childFrame) && - // $FlowFixMe childFrame.name === name && winFrames.indexOf(childFrame) !== -1 ) { return childFrame; } - } catch (err) { + } catch (err: unknown) { // pass } } try { - // $FlowFixMe + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument if (winFrames.indexOf(win.frames[name]) !== -1) { - // $FlowFixMe - return win.frames[name]; + // @ts-ignore + return win.frames[name] as CrossDomainWindowType; } - } catch (err) { + } catch (err: unknown) { // pass } try { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument if (winFrames.indexOf(win[name]) !== -1) { - return win[name]; + // @ts-ignore + return win[name] as CrossDomainWindowType; } - } catch (err) { + } catch (err: unknown) { // pass } } @@ -654,7 +663,7 @@ export function getFrameByName( export function findChildFrameByName( win: CrossDomainWindowType, name: string -): ?CrossDomainWindowType { +): CrossDomainWindowType | null | undefined { const frame = getFrameByName(win, name); if (frame) { @@ -673,7 +682,7 @@ export function findChildFrameByName( export function findFrameByName( win: CrossDomainWindowType, name: string -): ?CrossDomainWindowType { +): CrossDomainWindowType | null | undefined { const frame = getFrameByName(win, name); if (frame) { @@ -681,7 +690,6 @@ export function findFrameByName( } const top = getTop(win) || win; - return findChildFrameByName(top, name); } @@ -712,10 +720,9 @@ export function isOpener( } export function getAncestor( - win?: CrossDomainWindowType = window -): ?CrossDomainWindowType { + win: CrossDomainWindowType = window +): CrossDomainWindowType | null | undefined { win = win || window; - const opener = getOpener(win); if (opener) { @@ -731,13 +738,14 @@ export function getAncestor( export function getAncestors( win: CrossDomainWindowType -): $ReadOnlyArray { +): ReadonlyArray { const results = []; - let ancestor = win; while (ancestor) { + // @ts-ignore ancestor = getAncestor(ancestor); + if (ancestor) { results.push(ancestor); } @@ -777,19 +785,22 @@ export function isAncestor( return false; } -export function isPopup(win?: CrossDomainWindowType = window): boolean { +export function isPopup(win: CrossDomainWindowType = window): boolean { return Boolean(getOpener(win)); } -export function isIframe(win?: CrossDomainWindowType = window): boolean { +export function isIframe(win: CrossDomainWindowType = window): boolean { return Boolean(getParent(win)); } -export function isFullpage(win?: CrossDomainWindowType = window): boolean { +export function isFullpage(win: CrossDomainWindowType = window): boolean { return Boolean(!isIframe(win) && !isPopup(win)); } -function anyMatch(collection1, collection2): boolean { +function anyMatch( + collection1: ReadonlyArray, + collection2: ReadonlyArray +): boolean { for (const item1 of collection1) { for (const item2 of collection2) { if (item1 === item2) { @@ -808,7 +819,9 @@ export function getDistanceFromTop( let parent = win; while (parent) { + // @ts-ignore - trying to reassing to something that might be window parent = getParent(parent); + if (parent) { distance += 1; } @@ -819,8 +832,8 @@ export function getDistanceFromTop( export function getNthParent( win: CrossDomainWindowType, - n: number = 1 -): ?CrossDomainWindowType { + n = 1 +): CrossDomainWindowType | undefined | null { let parent = win; for (let i = 0; i < n; i++) { @@ -828,6 +841,7 @@ export function getNthParent( return; } + // @ts-ignore - trying to reassign to something that might be window parent = getParent(parent); } @@ -836,8 +850,8 @@ export function getNthParent( export function getNthParentFromTop( win: CrossDomainWindowType, - n: number = 1 -): ?CrossDomainWindowType { + n = 1 +): CrossDomainWindowType | null | undefined { return getNthParent(win, getDistanceFromTop(win) - n); } @@ -856,7 +870,7 @@ export function isSameTopWindow( return false; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -908,7 +922,8 @@ export function matchDomain( return false; } - // $FlowFixMe + // @ts-ignore - earlier already shortcutted the string case + // eslint-disable-next-line @typescript-eslint/no-unsafe-call return Boolean(origin.match(pattern)); } @@ -921,7 +936,9 @@ export function matchDomain( return false; } - return pattern.some((subpattern) => matchDomain(subpattern, origin)); + return pattern.some((subpattern: DomainMatcher) => + matchDomain(subpattern, origin) + ); } return false; @@ -947,24 +964,24 @@ export function getDomainFromUrl(url: string): string { } domain = domain.split("/").slice(0, 3).join("/"); - return domain; } export function onCloseWindow( win: CrossDomainWindowType, - callback: Function, - delay: number = 1000, - maxtime: number = Infinity -): {| cancel: () => void |} { - let timeout; + callback: (...args: Array) => void, + delay = 1000, + maxtime = Infinity +): { cancel: () => void } { + let timeout: NodeJS.Timeout; - const check = () => { + const check = (): void => { if (isWindowClosed(win)) { if (timeout) { clearTimeout(timeout); } + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression return callback(); } @@ -988,24 +1005,23 @@ export function onCloseWindow( } // eslint-disable-next-line complexity -export function isWindow(obj: Object): boolean { +export function isWindow(obj: unknown): boolean { try { if (obj === window) { return true; } - } catch (err) { - if (err && err.message === IE_WIN_ACCESS_ERROR) { + } catch (err: unknown) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return true; } } try { - // $FlowFixMe method-unbinding if (Object.prototype.toString.call(obj) === "[object Window]") { return true; } - } catch (err) { - if (err && err.message === IE_WIN_ACCESS_ERROR) { + } catch (err: unknown) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return true; } } @@ -1014,67 +1030,73 @@ export function isWindow(obj: Object): boolean { if (window.Window && obj instanceof window.Window) { return true; } - } catch (err) { - if (err && err.message === IE_WIN_ACCESS_ERROR) { + } catch (err: unknown) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return true; } } try { + // @ts-ignore not complete sure that obj has self. needs guard if (obj && obj.self === obj) { return true; } - } catch (err) { - if (err && err.message === IE_WIN_ACCESS_ERROR) { + } catch (err: unknown) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return true; } } try { + // @ts-ignore not complete sure that obj has parent. needs guard if (obj && obj.parent === obj) { return true; } - } catch (err) { - if (err && err.message === IE_WIN_ACCESS_ERROR) { + } catch (err: unknown) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return true; } } try { + // @ts-ignore not complete sure that obj has top. needs guard if (obj && obj.top === obj) { return true; } - } catch (err) { - if (err && err.message === IE_WIN_ACCESS_ERROR) { + } catch (err: unknown) { + if (err && (err as Error).message === IE_WIN_ACCESS_ERROR) { return true; } } try { - // eslint-disable-next-line no-self-compare + // @ts-ignore this equality check is a self compare + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression, no-self-compare if (noop(obj === obj) === "__unlikely_value__") { return false; } - } catch (err) { + } catch (err: unknown) { return true; } try { if ( obj && + // @ts-ignore more shenanigans obj.__cross_domain_utils_window_check__ === "__unlikely_value__" ) { return false; } - } catch (err) { + } catch (err: unknown) { return true; } try { + // @ts-ignore needs guard if ("postMessage" in obj && "self" in obj && "location" in obj) { return true; } - } catch (err) { + } catch (err: unknown) { // pass } @@ -1104,6 +1126,7 @@ export function normalizeMockUrl(url: string): string { return url; } + // @ts-ignore - global if (!__TEST__) { throw new Error(`Mock urls not supported out of test mode`); } @@ -1111,19 +1134,23 @@ export function normalizeMockUrl(url: string): string { return url.replace(/^mock:\/\/[^/]+/, getActualDomain(window)); } -export function getFrameForWindow(win: CrossDomainWindowType): ?HTMLElement { +export function getFrameForWindow( + win: CrossDomainWindowType +): HTMLElement | Element | null | undefined { if (isSameDomain(win)) { return assertSameDomain(win).frameElement; } + // @ts-ignore not an array type, but is iterable so fine for (const frame of document.querySelectorAll("iframe")) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (frame && frame.contentWindow && frame.contentWindow === win) { - return frame; + return frame as HTMLElement; } } } -export function closeWindow(win: CrossDomainWindowType) { +export function closeWindow(win: CrossDomainWindowType): void { if (isIframe(win)) { const frame = getFrameForWindow(win); if (frame && frame.parentElement) { diff --git a/test/closeWindow.test.ts b/test/closeWindow.test.ts new file mode 100644 index 0000000..53bcd0a --- /dev/null +++ b/test/closeWindow.test.ts @@ -0,0 +1,19 @@ +import { assert, test } from "vitest"; + +import { closeWindow } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("closeWindow will call window.close", () => { + let fnCalled = false; + const win = getSameDomainWindow({ + close: () => { + fnCalled = true; + }, + }); + + // @ts-ignore + closeWindow(win); + + assert(fnCalled, `Expected window.close to be called`); +}); diff --git a/test/getActualDomain.test.ts b/test/getActualDomain.test.ts new file mode 100644 index 0000000..cba7fd9 --- /dev/null +++ b/test/getActualDomain.test.ts @@ -0,0 +1,21 @@ +import { assert, test } from "vitest"; + +import { getActualDomain } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("getActualDomain should get the domain for a specific window", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: "foo.com:8087", + }, + }); + + const domain = getActualDomain(win); + const expectedDomain = `${win.location.protocol}//${win.location.host}`; + assert( + domain === expectedDomain, + `Expected domain to be "${expectedDomain}", got "${domain}"` + ); +}); diff --git a/test/getAllFramesInWindow.test.ts b/test/getAllFramesInWindow.test.ts new file mode 100644 index 0000000..f8eadf8 --- /dev/null +++ b/test/getAllFramesInWindow.test.ts @@ -0,0 +1,65 @@ +import { assert, test } from "vitest"; + +import { getAllFramesInWindow } from "../src"; + +test("getAllFramesInWindow should get all of the frames", () => { + const x: Record = { + name: "x", + }; + const y: Record = { + name: "y", + frames: [x], + }; + const a: Record = { + name: "a", + }; + const b: Record = { + name: "b", + frames: [a], + }; + const z: Record = { + name: "z", + frames: [b, y], + }; + x.top = z; + x.parent = y; + y.top = z; + y.parent = z; + a.top = z; + a.parent = b; + b.top = z; + b.parent = z; + z.top = z; + z.parent = z; + + const allFrames = [a, b, x, y, z]; + // @ts-ignore x is not type of window + const foundFrames = getAllFramesInWindow(x); + + assert( + foundFrames.length === allFrames.length, + `Expected to find ${allFrames.length}, but found ${foundFrames.length}` + ); + + for (const frame of allFrames) { + // @ts-ignore frame is not type of window + assert(foundFrames.includes(frame), `Did not find frame ${frame.name}`); + } +}); + +test("should get a mock frame defined in window.frames", () => { + const frames = window.frames; + const mockFrame = {}; + // @ts-ignore + window.frames = [mockFrame]; + const foundFrames = getAllFramesInWindow(window); + + assert( + // @ts-ignore + foundFrames.includes(mockFrame), + `getAllFramesInWindow expected to find mock frame in window.frames` + ); + + // @ts-ignore + window.frames = frames; +}); diff --git a/test/getDomain.test.ts b/test/getDomain.test.ts new file mode 100644 index 0000000..079f051 --- /dev/null +++ b/test/getDomain.test.ts @@ -0,0 +1,136 @@ +import { assert, test } from "vitest"; + +import { getDomain } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("getDomain should get the domain for the current window", () => { + // @ts-ignore + window.location = new URL("https://www.paypal.com/sdk/js"); + const domain = getDomain(); + const expectedDomain = `${window.location.protocol}//${window.location.host}`; + + assert( + domain === expectedDomain, + `Expected domain to be "${expectedDomain}", got "${domain}"` + ); +}); + +test("getDomain should get the domain for a specific window", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: "foo.com:8087", + }, + }); + const domain = getDomain(win); + const expectedDomain = `${win.location.protocol}//${win.location.host}`; + + assert( + domain === expectedDomain, + `Expected domain to be "${expectedDomain}", got "${domain}"` + ); +}); + +test("getDomain should get the mock domain for a specific window", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: "foo.com:8087", + }, + mockDomain: "mock://zomg.com:3456", + }); + const domain = getDomain(win); + const expectedDomain = "mock://zomg.com:3456"; + + assert( + domain === expectedDomain, + `Expected domain to be "${expectedDomain}", got "${domain}"` + ); +}); + +test("getDomain should get the actual domain for a specific window when mock domain is not mock://", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: "foo.com:8087", + }, + mockDomain: "mocc://zomg.com:3456", + }); + const domain = getDomain(win); + const expectedDomain = `${win.location.protocol}//${win.location.host}`; + + assert( + domain === expectedDomain, + `Expected domain to be "${expectedDomain}", got "${domain}"` + ); +}); + +test("getDomain should throw errors when the window does not have a location", () => { + const win = getSameDomainWindow({ + location: null, + mockDomain: "mocc://zomg.com:3456", + }); + let error; + + try { + getDomain(win); + } catch (err: unknown) { + error = err; + } + + assert(error instanceof Error, `Expected to get Error, got ${typeof error}`); +}); + +test("getDomain should throw errors when the window does not have a protocol", () => { + const win = getSameDomainWindow({ + location: { + protocol: null, + host: "foo.com:8087", + }, + mockDomain: "mocc://zomg.com:3456", + }); + let error; + + try { + getDomain(win); + } catch (err: unknown) { + error = err; + } + + assert(error instanceof Error, `Expected to get Error, got ${typeof error}`); +}); + +test("getDomain should throw errors when the window does not have a host", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: null, + }, + mockDomain: "mocc://zomg.com:3456", + }); + let error; + + try { + getDomain(win); + } catch (err: unknown) { + error = err; + } + + assert(error instanceof Error, `Expected to get Error, got ${typeof error}`); +}); + +test("getDomain should get the domain for a specific window when its protocol is file:// even with no host", () => { + const win = getSameDomainWindow({ + location: { + protocol: "file:", + }, + }); + const domain = getDomain(win); + const expectedDomain = `${win.location.protocol}//`; + + assert( + domain === expectedDomain, + `Expected domain to be "${expectedDomain}", got "${domain}"` + ); +}); diff --git a/test/getDomainFromUrl.test.ts b/test/getDomainFromUrl.test.ts new file mode 100644 index 0000000..7142364 --- /dev/null +++ b/test/getDomainFromUrl.test.ts @@ -0,0 +1,33 @@ +import { expect, test } from "vitest"; + +import { getDomainFromUrl } from "../src"; + +test("getDomainFromUrl should get url protocol and domain for a simple http url", () => { + const url = "http://github.com/krakenjs/cross-domain-utils/"; + const expected = "http://github.com"; + const domain = getDomainFromUrl(url); + expect(domain).toEqual(expected); +}); + +test("getDomainFromUrl should get url protocol and domain for a long https url", () => { + const url = + "https://www.google.com/search?q=Cross+Domain+utilities+npm&oq=cros&aqs=chrome.0.69i59l3j69i57j69i60l3j69i65.1620j0j7&sourceid=chrome&ie=UTF-8"; + const expected = "https://www.google.com"; + const domain = getDomainFromUrl(url); + expect(domain).toEqual(expected); +}); + +test("getDomainFromUrl should get url protocol and domain for a url with non-www subdomain", () => { + const url = "https://ca.indeed.com/"; + const expected = "https://ca.indeed.com"; + const domain = getDomainFromUrl(url); + expect(domain).toEqual(expected); +}); + +test("getDomainFromUrl should get url protocol and IP of a file url ", () => { + const url = "file://192.168.1.11/~User/TextFile.pdf"; // fake IP + + const expected = "file://192.168.1.11"; + const domain = getDomainFromUrl(url); + expect(domain).toEqual(expected); +}); diff --git a/test/getOpener.test.ts b/test/getOpener.test.ts new file mode 100644 index 0000000..7869f15 --- /dev/null +++ b/test/getOpener.test.ts @@ -0,0 +1,43 @@ +import { assert, test } from "vitest"; + +import { getOpener } from "../src"; + +import { getCrossDomainWindow } from "./utils"; + +test("getOpener should get the opener window if there is one", () => { + const win = getCrossDomainWindow({ + opener: {}, + }); + const opener = getOpener(win); + + assert(opener === win.opener, `Expected getOpener to return opener window`); +}); + +test("getOpener should not get the opener window if the window has a parent", () => { + const win = getCrossDomainWindow({ + parent: {}, + opener: {}, + }); + const opener = getOpener(win); + + assert(!opener, `Expected getOpener to not return a window`); +}); + +test("getOpener should not get the opener window if nothing is passed", () => { + const opener = getOpener(); + + assert(!opener, `Expected getOpener to not return a window`); +}); + +test("getOpener should return void in case of any errors", () => { + const win = getCrossDomainWindow({}); + + Object.defineProperty(win, "opener", { + get() { + throw new Error("error"); + }, + }); + const opener = getOpener(win); + + assert(!opener, `Expected getOpener to not return a window`); +}); diff --git a/test/getParent.test.ts b/test/getParent.test.ts new file mode 100644 index 0000000..6bddf0b --- /dev/null +++ b/test/getParent.test.ts @@ -0,0 +1,36 @@ +import { assert, test } from "vitest"; + +import { getParent } from "../src"; + +import { getCrossDomainWindow } from "./utils"; + +test("getParent should get the parent window if there is one", () => { + const win = getCrossDomainWindow({ + parent: {}, + }); + const parent = getParent(win); + + assert(parent === win.parent, `Expected getParent to return parent window`); +}); + +test("getParent should not get the parent window if the parent is the same window", () => { + const win = getCrossDomainWindow({}); + // @ts-ignore + win.parent = win; + const parent = getParent(win); + + assert(!parent, `Expected getParent to not return a window`); +}); + +test("getParent should return void in case of any errors", () => { + const win = getCrossDomainWindow({}); + + Object.defineProperty(win, "parent", { + get() { + throw new Error("error"); + }, + }); + const parent = getParent(win); + + assert(!parent, `Expected getParent to not return a window`); +}); diff --git a/test/getParents.test.ts b/test/getParents.test.ts new file mode 100644 index 0000000..f4582a2 --- /dev/null +++ b/test/getParents.test.ts @@ -0,0 +1,36 @@ +import { assert, test } from "vitest"; + +import { getParents } from "../src"; + +import { getCrossDomainWindow } from "./utils"; + +test("getParents should get all of a windows parents", () => { + const win = getCrossDomainWindow({ + parent: { + parent: { + parent: {}, + }, + }, + }); + + // @ts-ignore + win.parent.parent.parent.parent = win.parent.parent.parent; + const parents = getParents(win); + + assert( + parents.length === 3, + `Expected to get 3 parents, got ${parents.length}` + ); + assert( + parents[0] === win.parent, + `Expected correct parent window to be returned at index 0` + ); + assert( + parents[1] === win.parent.parent, + `Expected correct parent window to be returned at index 1` + ); + assert( + parents[2] === win.parent.parent.parent, + `Expected correct parent window to be returned at index 2` + ); +}); diff --git a/test/getUserAgent.test.ts b/test/getUserAgent.test.ts new file mode 100644 index 0000000..18b41c3 --- /dev/null +++ b/test/getUserAgent.test.ts @@ -0,0 +1,37 @@ +import { assert, test } from "vitest"; + +import { getUserAgent } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("getUserAgent should get the real user agent", () => { + const userAgent = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"; + const win = getSameDomainWindow({ + navigator: { + userAgent, + }, + }); + const result = getUserAgent(win); + + assert( + result === userAgent, + `Expected getUserAgent to return ${userAgent}, actually got ${result}` + ); +}); + +test("getUserAgent should get the mock user agent", () => { + const mockUserAgent = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"; + const win = getSameDomainWindow({ + navigator: { + mockUserAgent, + }, + }); + const result = getUserAgent(win); + + assert( + result === mockUserAgent, + `Expected getUserAgent to return ${mockUserAgent}, actually got ${result}` + ); +}); diff --git a/test/index.js b/test/index.js deleted file mode 100644 index f5d7518..0000000 --- a/test/index.js +++ /dev/null @@ -1,5 +0,0 @@ -/* @flow */ - -import "./util"; - -import "./tests"; diff --git a/test/isBlankDomain.test.ts b/test/isBlankDomain.test.ts new file mode 100644 index 0000000..f67562a --- /dev/null +++ b/test/isBlankDomain.test.ts @@ -0,0 +1,53 @@ +import { assert, test } from "vitest"; + +import { isBlankDomain } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("isBlankDomain returns true if window.href is falsy", () => { + const falsyValues = [0, "", null, undefined, NaN]; + const windows = falsyValues.map((falsyValue) => + getSameDomainWindow({ + location: { + href: falsyValue, + }, + }) + ); + // @ts-ignore + const results = windows.map(isBlankDomain); + const expectedResult = true; + assert( + results.every((result) => result === expectedResult), + `Expected isBlankDomain result to return ${expectedResult.toString()}` + ); +}); + +test("isBlankDomain returns true if window.href about:blank", () => { + const win = getSameDomainWindow({ + location: { + href: "about:blank", + }, + }); + const expectedResult = true; + // @ts-ignore + const result = isBlankDomain(win); + assert( + result === expectedResult, + `Expected isBlankDomain result to be ${expectedResult.toString()}, got ${result.toString()}` + ); +}); + +test("isBlankDomain should return false if window.href is truthy but not about:blank", () => { + const win = getSameDomainWindow({ + location: { + href: "someUrl", + }, + }); + const expectedResult = false; + // @ts-ignore + const result = isBlankDomain(win); + assert( + result === expectedResult, + `Expected isBlankDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); diff --git a/test/isFileProtocol.test.ts b/test/isFileProtocol.test.ts new file mode 100644 index 0000000..000f6f6 --- /dev/null +++ b/test/isFileProtocol.test.ts @@ -0,0 +1,33 @@ +import { assert, test } from "vitest"; + +import { isFileProtocol, PROTOCOL } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("isFileProtocol will return true when window.location is pointing to a file", () => { + const win = getSameDomainWindow({ + location: { + protocol: PROTOCOL.FILE, + }, + }); + const expectedResult = true; + const result = isFileProtocol(win); + assert( + result === expectedResult, + `Expected result to be ${expectedResult.toString()} but received ${result.toString()}` + ); +}); + +test("isFileProtocol will return true when window.location is pointing to a file", () => { + const win = getSameDomainWindow({ + location: { + protocol: PROTOCOL.ABOUT, + }, + }); + const expectedResult = false; + const result = isFileProtocol(win); + assert( + result === expectedResult, + `Expected result to be ${expectedResult.toString()} but received ${result.toString()}` + ); +}); diff --git a/test/isSameDomain.test.ts b/test/isSameDomain.test.ts new file mode 100644 index 0000000..84a41ad --- /dev/null +++ b/test/isSameDomain.test.ts @@ -0,0 +1,212 @@ +import { assert, test } from "vitest"; + +import { isSameDomain } from "../src"; + +import { getSameDomainWindow } from "./utils"; + +test("isSameDomain should give a positive result for isSameDomain", () => { + // @ts-ignore + window.location = new URL("http://www.paypal.com/sdk/js"); + + const win = getSameDomainWindow({ + location: { + protocol: window.location.protocol, + host: window.location.host, + }, + }); + + const result = isSameDomain(win); + const expectedResult = true; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain with a different protocol", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: window.location.host, + }, + }); + + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain with a different host", () => { + const win = getSameDomainWindow({ + location: { + protocol: window.location.protocol, + host: "foobar.com:12345", + }, + }); + + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain with a different protocol and host", () => { + const win = getSameDomainWindow({ + location: { + protocol: "https:", + host: "foobar.com:12345", + }, + }); + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain when an error is thrown on protocol", () => { + const win = getSameDomainWindow({ + location: { + get protocol() { + throw new Error("error"); + }, + + host: window.location.host, + }, + }); + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain when an error is thrown on host", () => { + const win = getSameDomainWindow({ + location: { + protocol: window.location.protocol, + + get host() { + throw new Error("error"); + }, + }, + }); + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain when location is non-enumerable", () => { + const win = getSameDomainWindow({}); + Object.defineProperty(win, "location", { + value: { + protocol: window.location.protocol, + host: window.location.host, + }, + enumerable: false, + }); + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a positive result for isSameDomain when mockDomain matches", () => { + // @ts-ignore + window.mockDomain = "mock://foobar.com:12345"; + // @ts-ignore + window.location = new URL("mock://foobar.com:12345"); + + const win = getSameDomainWindow({ + location: { + protocol: window.location.protocol, + host: window.location.host, + }, + mockDomain: "mock://foobar.com:12345", + }); + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = true; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); + +test("isSameDomain should give a negative result for isSameDomain when mockDomain does not match", () => { + // @ts-ignore + window.mockDomain = "mock://fizzbuzz.com:345"; + const win = getSameDomainWindow({ + location: { + protocol: window.location.protocol, + host: window.location.host, + }, + mockDomain: "mock://foobar.com:12345", + }); + Object.defineProperty(win.location, "href", { + get(): string { + return `${win.location.protocol}//${win.location.host}`; + }, + }); + const result = isSameDomain(win); + const expectedResult = false; + + assert( + result === expectedResult, + `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` + ); +}); diff --git a/test/matchDomain.test.ts b/test/matchDomain.test.ts new file mode 100644 index 0000000..5888a27 --- /dev/null +++ b/test/matchDomain.test.ts @@ -0,0 +1,19 @@ +import { assert, test } from "vitest"; + +import { matchDomain } from "../src"; + +test("matchDomain returns true when domain is a string and origin is a string", () => { + assert.isTrue(matchDomain(["http://domain"], "http://domain")); +}); + +test("matchDomain returns true when domain is regex and origin is a string", () => { + assert.isTrue(matchDomain(/^http:\/\/domain$/, "http://domain")); +}); + +test("matchDomain returns true when domain is array with a wildcard and origin is a string", () => { + assert.isTrue(matchDomain(["*"], "http://domain")); +}); + +test("matchDomain returns true when domain is array of strings and origin is a string", () => { + assert.isTrue(matchDomain(["http://domain"], "http://domain")); +}); diff --git a/test/stringifyDomainPattern.test.ts b/test/stringifyDomainPattern.test.ts new file mode 100644 index 0000000..2461a0d --- /dev/null +++ b/test/stringifyDomainPattern.test.ts @@ -0,0 +1,28 @@ +import { expect, test } from "vitest"; + +import { stringifyDomainPattern } from "../src"; + +test("should stringify a single regex expression to RegExp", () => { + const pattern = /[a-zA-Z0-9]{1,5}/; + const domainPatternStringified = stringifyDomainPattern(pattern); + const expected = `RegExp(${pattern.toString()})`; + + if (typeof domainPatternStringified !== "string") { + throw new TypeError( + `Expected domainPattern to be string, instead got ${typeof domainPatternStringified}` + ); + } + + expect(domainPatternStringified).to.equal(expected); +}); + +test("should stringify an array of domain patterns to RegExp", () => { + const p1 = "/[a-zA-Z0-9]{1,5}/"; + const p2 = "/\\.[a-zA-Z]{2,}$/"; + const domainPatternsArray = [p1, p2]; + const domainPatternsArrayStringified = + stringifyDomainPattern(domainPatternsArray); + const expected = `(${p1} | ${p2})`; + + expect(domainPatternsArrayStringified).to.equal(expected); +}); diff --git a/test/tests/closeWindow.js b/test/tests/closeWindow.js deleted file mode 100644 index fe1dc4e..0000000 --- a/test/tests/closeWindow.js +++ /dev/null @@ -1,22 +0,0 @@ -/* @flow */ - -import { assert } from "chai"; - -import { closeWindow } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("closeWindow", () => { - it("will call window.close", () => { - let fnCalled = false; - - const win = getSameDomainWindow({ - close: () => { - fnCalled = true; - }, - }); - const expectedResult = true; - closeWindow(win); - - assert(fnCalled === expectedResult, `Expected window.close to be called`); - }); -}); diff --git a/test/tests/getActualDomain.js b/test/tests/getActualDomain.js deleted file mode 100644 index 784334a..0000000 --- a/test/tests/getActualDomain.js +++ /dev/null @@ -1,24 +0,0 @@ -/* @flow */ - -import { getActualDomain } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("getActualDomain cases", () => { - it("should get the domain for a specific window", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: "foo.com:8087", - }, - }); - - const domain = getActualDomain(win); - const expectedDomain = `${win.location.protocol}//${win.location.host}`; - - if (domain !== expectedDomain) { - throw new Error( - `Expected domain to be "${expectedDomain}", got "${domain}"` - ); - } - }); -}); diff --git a/test/tests/getAllFramesInWindow.js b/test/tests/getAllFramesInWindow.js deleted file mode 100644 index 5c7be5e..0000000 --- a/test/tests/getAllFramesInWindow.js +++ /dev/null @@ -1,63 +0,0 @@ -/* @flow */ - -import { getAllFramesInWindow } from "../../src"; - -describe("getAllFramesInWindow cases", () => { - it("should get all of the frames", () => { - const x: Object = { name: "x" }; - const y: Object = { name: "y", frames: [x] }; - - const a: Object = { name: "a" }; - const b: Object = { name: "b", frames: [a] }; - - const z: Object = { name: "z", frames: [b, y] }; - - x.top = z; - x.parent = y; - - y.top = z; - y.parent = z; - - a.top = z; - a.parent = b; - - b.top = z; - b.parent = z; - - z.top = z; - z.parent = z; - - const allFrames = [a, b, x, y, z]; - const foundFrames = getAllFramesInWindow(x); - - if (foundFrames.length !== allFrames.length) { - throw new Error( - `Expected to find ${allFrames.length}, but found ${foundFrames.length}` - ); - } - - for (const frame of allFrames) { - if (foundFrames.indexOf(frame) === -1) { - throw new Error(`Did not find frame ${frame.name}`); - } - } - }); - - it("should get a mock frame defined in window.frames", () => { - const frames = window.frames; - - const mockFrame = {}; - - window.frames = [mockFrame]; - - const foundFrames = getAllFramesInWindow(window); - - if (foundFrames.indexOf(mockFrame) === -1) { - throw new Error( - `getAllFramesInWindow expected to find mock frame in window.frames` - ); - } - - window.frames = frames; - }); -}); diff --git a/test/tests/getDomain.js b/test/tests/getDomain.js deleted file mode 100644 index c42351e..0000000 --- a/test/tests/getDomain.js +++ /dev/null @@ -1,153 +0,0 @@ -/* @flow */ - -import { getDomain } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("getDomain cases", () => { - it("should get the domain for the current window", () => { - const domain = getDomain(); - const expectedDomain = `${window.location.protocol}//${window.location.host}`; - - if (domain !== expectedDomain) { - throw new Error( - `Expected domain to be "${expectedDomain}", got "${domain}"` - ); - } - }); - - it("should get the domain for a specific window", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: "foo.com:8087", - }, - }); - - const domain = getDomain(win); - const expectedDomain = `${win.location.protocol}//${win.location.host}`; - - if (domain !== expectedDomain) { - throw new Error( - `Expected domain to be "${expectedDomain}", got "${domain}"` - ); - } - }); - - it("should get the mock domain for a specific window", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: "foo.com:8087", - }, - mockDomain: "mock://zomg.com:3456", - }); - - const domain = getDomain(win); - const expectedDomain = "mock://zomg.com:3456"; - - if (domain !== expectedDomain) { - throw new Error( - `Expected domain to be "${expectedDomain}", got "${domain}"` - ); - } - }); - - it("should get the actual domain for a specific window when mock domain is not mock://", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: "foo.com:8087", - }, - mockDomain: "mocc://zomg.com:3456", - }); - - const domain = getDomain(win); - const expectedDomain = `${win.location.protocol}//${win.location.host}`; - - if (domain !== expectedDomain) { - throw new Error( - `Expected domain to be "${expectedDomain}", got "${domain}"` - ); - } - }); - - it("should throw errors when the window does not have a location", () => { - const win = getSameDomainWindow({ - location: null, - mockDomain: "mocc://zomg.com:3456", - }); - - let error; - - try { - getDomain(win); - } catch (err) { - error = err; - } - - if (!(error instanceof Error)) { - throw new TypeError(`Expected to get Error, got ${typeof error}`); - } - }); - - it("should throw errors when the window does not have a protocol", () => { - const win = getSameDomainWindow({ - location: { - protocol: null, - host: "foo.com:8087", - }, - mockDomain: "mocc://zomg.com:3456", - }); - - let error; - - try { - getDomain(win); - } catch (err) { - error = err; - } - - if (!(error instanceof Error)) { - throw new TypeError(`Expected to get Error, got ${typeof error}`); - } - }); - - it("should throw errors when the window does not have a host", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: null, - }, - mockDomain: "mocc://zomg.com:3456", - }); - - let error; - - try { - getDomain(win); - } catch (err) { - error = err; - } - - if (!(error instanceof Error)) { - throw new TypeError(`Expected to get Error, got ${typeof error}`); - } - }); - - it("should get the domain for a specific window when its protocol is file:// even with no host", () => { - const win = getSameDomainWindow({ - location: { - protocol: "file:", - }, - }); - - const domain = getDomain(win); - const expectedDomain = `${win.location.protocol}//`; - - if (domain !== expectedDomain) { - throw new Error( - `Expected domain to be "${expectedDomain}", got "${domain}"` - ); - } - }); -}); diff --git a/test/tests/getDomainFromUrl.js b/test/tests/getDomainFromUrl.js deleted file mode 100644 index f6d673e..0000000 --- a/test/tests/getDomainFromUrl.js +++ /dev/null @@ -1,43 +0,0 @@ -/* @flow */ - -import { expect } from "chai"; - -import { getDomainFromUrl } from "../../src"; - -describe("getDomainFromUrl test cases ", () => { - it("should get url protocol and domain for a simple http url", () => { - const url = "http://github.com/krakenjs/cross-domain-utils/"; - const expected = "http://github.com"; - - const domain = getDomainFromUrl(url); - expect(domain).to.equal(expected); - }); - - it("should get url protocol and domain for a long https url", () => { - const url = - "https://www.google.com/search?q=Cross+Domain+utilities+npm&oq=cros&aqs=chrome.0.69i59l3j69i57j69i60l3j69i65.1620j0j7&sourceid=chrome&ie=UTF-8"; - const expected = "https://www.google.com"; - - const domain = getDomainFromUrl(url); - - expect(domain).to.equal(expected); - }); - - it("should get url protocol and domain for a url with non-www subdomain", () => { - const url = "https://ca.indeed.com/"; - const expected = "https://ca.indeed.com"; - - const domain = getDomainFromUrl(url); - - expect(domain).to.equal(expected); - }); - - it("should get url protocol and IP of a file url ", () => { - const url = "file://192.168.1.11/~User/TextFile.pdf"; // fake IP - const expected = "file://192.168.1.11"; - - const domain = getDomainFromUrl(url); - - expect(domain).to.equal(expected); - }); -}); diff --git a/test/tests/getOpener.js b/test/tests/getOpener.js deleted file mode 100644 index 5fdfa4c..0000000 --- a/test/tests/getOpener.js +++ /dev/null @@ -1,56 +0,0 @@ -/* @flow */ - -import { getOpener } from "../../src"; -import { getCrossDomainWindow } from "../win"; - -describe("getOpener cases", () => { - it("should get the opener window if there is one", () => { - const win = getCrossDomainWindow({ - opener: {}, - }); - - const opener = getOpener(win); - - if (opener !== win.opener) { - throw new Error(`Expected getOpener to return opener window`); - } - }); - - it("should not get the opener window if the window has a parent", () => { - const win = getCrossDomainWindow({ - parent: {}, - opener: {}, - }); - - const opener = getOpener(win); - - if (opener) { - throw new Error(`Expected getOpener to not return a window`); - } - }); - - it("should not get the opener window if nothing is passed", () => { - const opener = getOpener(); - - if (opener) { - throw new Error(`Expected getOpener to not return a window`); - } - }); - - it("should return void in case of any errors", () => { - const win = getCrossDomainWindow({}); - - // $FlowFixMe - Object.defineProperty(win, "opener", { - get() { - throw new Error("error"); - }, - }); - - const opener = getOpener(win); - - if (opener) { - throw new Error(`Expected getOpener to not return a window`); - } - }); -}); diff --git a/test/tests/getParent.js b/test/tests/getParent.js deleted file mode 100644 index 62b036f..0000000 --- a/test/tests/getParent.js +++ /dev/null @@ -1,46 +0,0 @@ -/* @flow */ - -import { getParent } from "../../src"; -import { getCrossDomainWindow } from "../win"; - -describe("getParent cases", () => { - it("should get the parent window if there is one", () => { - const win = getCrossDomainWindow({ - parent: {}, - }); - - const parent = getParent(win); - - if (parent !== win.parent) { - throw new Error(`Expected getParent to return parent window`); - } - }); - - it("should not get the parent window if the parent is the same window", () => { - const win = getCrossDomainWindow({}); - win.parent = win; - - const parent = getParent(win); - - if (parent) { - throw new Error(`Expected getParent to not return a window`); - } - }); - - it("should return void in case of any errors", () => { - const win = getCrossDomainWindow({}); - - // $FlowFixMe - Object.defineProperty(win, "parent", { - get() { - throw new Error("error"); - }, - }); - - const parent = getParent(win); - - if (parent) { - throw new Error(`Expected getParent to not return a window`); - } - }); -}); diff --git a/test/tests/getParents.js b/test/tests/getParents.js deleted file mode 100644 index d8d0753..0000000 --- a/test/tests/getParents.js +++ /dev/null @@ -1,42 +0,0 @@ -/* @flow */ - -import { getParents } from "../../src"; -import { getCrossDomainWindow } from "../win"; - -describe("getParents cases", () => { - it("should get all of a windows parents", () => { - const win = getCrossDomainWindow({ - parent: { - parent: { - parent: {}, - }, - }, - }); - - win.parent.parent.parent.parent = win.parent.parent.parent; - - const parents = getParents(win); - - if (parents.length !== 3) { - throw new Error(`Expected to get 3 parents, got ${parents.length}`); - } - - if (parents[0] !== win.parent) { - throw new Error( - `Expected correct parent window to be returned at index 0` - ); - } - - if (parents[1] !== win.parent.parent) { - throw new Error( - `Expected correct parent window to be returned at index 1` - ); - } - - if (parents[2] !== win.parent.parent.parent) { - throw new Error( - `Expected correct parent window to be returned at index 2` - ); - } - }); -}); diff --git a/test/tests/getUserAgent.js b/test/tests/getUserAgent.js deleted file mode 100644 index 355e0af..0000000 --- a/test/tests/getUserAgent.js +++ /dev/null @@ -1,44 +0,0 @@ -/* @flow */ - -import { getUserAgent } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("getUserAgent cases", () => { - it("should get the real user agent", () => { - const userAgent = - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"; - - const win = getSameDomainWindow({ - navigator: { - userAgent, - }, - }); - - const result = getUserAgent(win); - - if (result !== userAgent) { - throw new Error( - `Expected getUserAgent to return ${userAgent}, actually got ${result}` - ); - } - }); - - it("should get the mock user agent", () => { - const mockUserAgent = - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"; - - const win = getSameDomainWindow({ - navigator: { - mockUserAgent, - }, - }); - - const result = getUserAgent(win); - - if (result !== mockUserAgent) { - throw new Error( - `Expected getUserAgent to return ${mockUserAgent}, actually got ${result}` - ); - } - }); -}); diff --git a/test/tests/index.js b/test/tests/index.js deleted file mode 100644 index 5ba2867..0000000 --- a/test/tests/index.js +++ /dev/null @@ -1,16 +0,0 @@ -/* @flow */ - -import "./getActualDomain"; -import "./getDomain"; -import "./isSameDomain"; -import "./matchDomain"; -import "./getParent"; -import "./getOpener"; -import "./getParents"; -import "./getAllFramesInWindow"; -import "./getUserAgent"; -import "./stringifyDomainPattern"; -import "./getDomainFromUrl"; -import "./isBlankDomain"; -import "./closeWindow"; -import "./isFileProtocol"; diff --git a/test/tests/isBlankDomain.js b/test/tests/isBlankDomain.js deleted file mode 100644 index 710bcf2..0000000 --- a/test/tests/isBlankDomain.js +++ /dev/null @@ -1,57 +0,0 @@ -/* @flow */ - -import { assert } from "chai"; - -import { isBlankDomain } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("isBlankDomain", () => { - it("returns true if window.href is falsy", () => { - const falsyValues = [0, "", null, undefined, NaN]; - const windows = falsyValues.map((falsyValue) => - getSameDomainWindow({ - location: { - href: falsyValue, - }, - }) - ); - - const results = windows.map(isBlankDomain); - const expectedResult = true; - - assert( - results.every((result) => result === expectedResult), - `Expected isBlankDomain result to return ${expectedResult.toString()}` - ); - }); - - it("returns true if window.href about:blank", () => { - const win = getSameDomainWindow({ - location: { - href: "about:blank", - }, - }); - const expectedResult = true; - const result = isBlankDomain(win); - - assert( - result === expectedResult, - `Expected isBlankDomain result to be ${expectedResult.toString()}, got ${result.toString()}` - ); - }); - - it("should return false if window.href is truthy but not about:blank", () => { - const win = getSameDomainWindow({ - location: { - href: "someUrl", - }, - }); - const expectedResult = false; - const result = isBlankDomain(win); - - assert( - result === expectedResult, - `Expected isBlankDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - }); -}); diff --git a/test/tests/isFileProtocol.js b/test/tests/isFileProtocol.js deleted file mode 100644 index 25a45bb..0000000 --- a/test/tests/isFileProtocol.js +++ /dev/null @@ -1,38 +0,0 @@ -/* @flow */ - -import { assert } from "chai"; - -import { isFileProtocol, PROTOCOL } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("isFileProtocol", () => { - it("will return true when window.location is pointing to a file", () => { - const win = getSameDomainWindow({ - location: { - protocol: PROTOCOL.FILE, - }, - }); - const expectedResult = true; - const result = isFileProtocol(win); - - assert( - result === expectedResult, - `Expected result to be ${expectedResult.toString()} but received ${result.toString()}` - ); - }); - - it("will return true when window.location is pointing to a file", () => { - const win = getSameDomainWindow({ - location: { - protocol: PROTOCOL.ABOUT, - }, - }); - const expectedResult = false; - const result = isFileProtocol(win); - - assert( - result === expectedResult, - `Expected result to be ${expectedResult.toString()} but received ${result.toString()}` - ); - }); -}); diff --git a/test/tests/isSameDomain.js b/test/tests/isSameDomain.js deleted file mode 100644 index b38c7c5..0000000 --- a/test/tests/isSameDomain.js +++ /dev/null @@ -1,241 +0,0 @@ -/* @flow */ - -import { isSameDomain } from "../../src"; -import { getSameDomainWindow } from "../win"; - -describe("isSameDomain cases", () => { - it("should give a positive result for isSameDomain", () => { - const win = getSameDomainWindow({ - location: { - protocol: window.location.protocol, - host: window.location.host, - }, - }); - - const result = isSameDomain(win); - const expectedResult = true; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a negative result for isSameDomain with a different protocol", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: window.location.host, - }, - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a negative result for isSameDomain with a different host", () => { - const win = getSameDomainWindow({ - location: { - protocol: window.location.protocol, - host: "foobar.com:12345", - }, - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a negative result for isSameDomain with a different protocol and host", () => { - const win = getSameDomainWindow({ - location: { - protocol: "https:", - host: "foobar.com:12345", - }, - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a negative result for isSameDomain when an error is thrown on protocol", () => { - const win = getSameDomainWindow({ - location: { - get protocol() { - throw new Error("error"); - }, - host: window.location.host, - }, - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a negative result for isSameDomain when an error is thrown on host", () => { - const win = getSameDomainWindow({ - location: { - protocol: window.location.protocol, - get host() { - throw new Error("error"); - }, - }, - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a negative result for isSameDomain when location is non-enumerable", () => { - const win = getSameDomainWindow({}); - - Object.defineProperty(win, "location", { - value: { - protocol: window.location.protocol, - host: window.location.host, - }, - enumerable: false, - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - }); - - it("should give a positive result for isSameDomain when mockDomain matches", () => { - window.mockDomain = "mock://foobar.com:12345"; - - const win = getSameDomainWindow({ - location: { - protocol: window.location.protocol, - host: window.location.host, - }, - mockDomain: "mock://foobar.com:12345", - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = true; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - - delete window.mockDomain; - }); - - it("should give a negative result for isSameDomain when mockDomain does not match", () => { - window.mockDomain = "mock://fizzbuzz.com:345"; - - const win = getSameDomainWindow({ - location: { - protocol: window.location.protocol, - host: window.location.host, - }, - mockDomain: "mock://foobar.com:12345", - }); - - // $FlowFixMe - Object.defineProperty(win.location, "href", { - get(): string { - return `${win.location.protocol}//${win.location.host}`; - }, - }); - - const result = isSameDomain(win); - const expectedResult = false; - - if (result !== expectedResult) { - throw new Error( - `Expected isSameDomain result to be "${expectedResult.toString()}", got "${result.toString()}"` - ); - } - - delete window.mockDomain; - }); -}); diff --git a/test/tests/matchDomain.js b/test/tests/matchDomain.js deleted file mode 100644 index 5f3f0bd..0000000 --- a/test/tests/matchDomain.js +++ /dev/null @@ -1,25 +0,0 @@ -/* @flow */ - -import { assert } from "chai"; - -import { matchDomain } from "../../src"; - -describe("match domain", () => { - describe("returns true when", () => { - it("domain is a string and origin is a string", () => { - assert.isTrue(matchDomain(["http://domain"], "http://domain")); - }); - - it("domain is regex and origin is a string", () => { - assert.isTrue(matchDomain(/^http:\/\/domain$/, "http://domain")); - }); - - it("domain is array with a wildcard and origin is a string", () => { - assert.isTrue(matchDomain(["*"], "http://domain")); - }); - - it("domain is array of strings and origin is a string", () => { - assert.isTrue(matchDomain(["http://domain"], "http://domain")); - }); - }); -}); diff --git a/test/tests/stringifyDomainPattern.js b/test/tests/stringifyDomainPattern.js deleted file mode 100644 index 0f08b79..0000000 --- a/test/tests/stringifyDomainPattern.js +++ /dev/null @@ -1,35 +0,0 @@ -/* @flow */ - -import { expect } from "chai"; - -import { stringifyDomainPattern } from "../../src"; - -describe("stringifyDomainPattern test cases ", () => { - it("should stringify a single regex expression to RegExp", () => { - const pattern = /[a-zA-Z0-9]{1,5}/; - const domainPatternStringified = stringifyDomainPattern(pattern); - - const expected = `RegExp(${pattern.toString()})`; - - if (typeof domainPatternStringified !== "string") { - throw new TypeError( - `Expected domainPattern to be string, instead got ${typeof domainPatternStringified}` - ); - } - - expect(domainPatternStringified).to.equal(expected); - }); - - it("should stringify an array of domain patterns to RegExp", () => { - const p1 = "/[a-zA-Z0-9]{1,5}/"; - const p2 = "/\\.[a-zA-Z]{2,}$/"; - const domainPatternsArray = [p1, p2]; - - const domainPatternsArrayStringified = - stringifyDomainPattern(domainPatternsArray); - - const expected = `(${p1} | ${p2})`; - - expect(domainPatternsArrayStringified).to.equal(expected); - }); -}); diff --git a/test/util.js b/test/util.js deleted file mode 100644 index 8f2c851..0000000 --- a/test/util.js +++ /dev/null @@ -1,11 +0,0 @@ -/* @flow */ - -window.console.karma = (...args) => { - const karma = - window.karma || - (window.top && window.top.karma) || - (window.opener && window.opener.karma); - karma.log("debug", ...args); - // eslint-disable-next-line no-console - console.log.apply(console, args); -}; diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 0000000..ae95432 --- /dev/null +++ b/test/utils.ts @@ -0,0 +1,18 @@ +import type { CrossDomainWindowType, SameDomainWindowType } from "../src/types"; + +export function getCrossDomainWindow( + options: Record +): CrossDomainWindowType { + // @ts-ignore not a true window type + return { + ...options, + }; +} +export function getSameDomainWindow( + options: Record +): CrossDomainWindowType | SameDomainWindowType | Window { + // @ts-ignore not a true window type + return { + ...options, + }; +} diff --git a/test/win.js b/test/win.js deleted file mode 100644 index 29aae70..0000000 --- a/test/win.js +++ /dev/null @@ -1,15 +0,0 @@ -/* @flow */ - -import type { CrossDomainWindowType, SameDomainWindowType } from "../src/types"; - -export function getCrossDomainWindow(options: Object): CrossDomainWindowType { - return { - ...options, - }; -} - -export function getSameDomainWindow(options: Object): SameDomainWindowType { - return { - ...options, - }; -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a071093 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@krakenjs/grumbler-scripts/config/tsconfig.json" +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..5883cfd --- /dev/null +++ b/vite.config.js @@ -0,0 +1,31 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable spaced-comment */ +/// + +// Configure Vitest (https://vitest.dev/config/) + +import path from "path"; + +import { defineConfig } from "vite"; + +// eslint-disable-next-line import/no-default-export +export default defineConfig({ + build: { + lib: { + entry: path.resolve(__dirname, "src/index.ts"), + name: "crossDomainUtil", + fileName: (format) => `cross-domain-utils.${format}.js`, + formats: ["es", "umd"], + }, + sourcemap: true, + rollupOptions: { + ouput: { + preserveModules: true, + }, + }, + }, + test: { + /* for example, use global to avoid globals imports (describe, test, expect): */ + // globals: true, + }, +}); diff --git a/webpack.config.ts b/webpack.config.ts new file mode 100644 index 0000000..4f9f52e --- /dev/null +++ b/webpack.config.ts @@ -0,0 +1,39 @@ +import { getWebpackConfig } from "@krakenjs/webpack-config-grumbler"; + +const FILE_NAME = "cross-domain-utils"; +const MODULE_NAME = "crossDomainUtils"; + +export const WEBPACK_CONFIG = getWebpackConfig({ + entry: "./src/index.ts", + filename: `${FILE_NAME}.js`, + modulename: MODULE_NAME, + minify: false, + vars: { + __MIN__: false, + __TEST__: false, + }, +}); + +export const WEBPACK_CONFIG_MIN = getWebpackConfig({ + entry: "./src/index.ts", + filename: `${FILE_NAME}.min.js`, + modulename: MODULE_NAME, + minify: true, + vars: { + __MIN__: true, + __TEST__: false, + }, +}); + +export const WEBPACK_CONFIG_TEST = getWebpackConfig({ + entry: "./src/index.ts", + modulename: MODULE_NAME, + options: { + devtool: "inline-source-map", + }, + vars: { + __TEST__: true, + }, +}); + +export default [WEBPACK_CONFIG, WEBPACK_CONFIG_MIN]; From ce8952112d1ca8889ddc8f19a7e67797e17a0517 Mon Sep 17 00:00:00 2001 From: Westin Wrzesinski Date: Tue, 22 Nov 2022 13:32:00 -0600 Subject: [PATCH 2/4] fix: lintable --- .eslintrc.js | 6 ++---- package.json | 30 +++++++++++++++++------------- src/util.ts | 1 - src/utils.ts | 10 ++++------ tsconfig.json | 2 +- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 8a8b1ce..84bcf1c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,10 +2,8 @@ module.exports = { extends: "./node_modules/@krakenjs/eslint-config-grumbler/eslintrc-typescript", - globals: { - __TEST__: true, - }, - rules: { + "keyword-spacing": "off", + "@typescript-eslint/keyword-spacing": "off", }, }; diff --git a/package.json b/package.json index f10c0e8..b59ea13 100644 --- a/package.json +++ b/package.json @@ -80,22 +80,26 @@ ], "readmeFilename": "README.md", "devDependencies": { - "@commitlint/cli": "^16.2.1", - "@commitlint/config-conventional": "^16.2.1", - "@krakenjs/grumbler-scripts": "^8.0.5", - "@vitest/ui": "^0.16.0", - "c8": "^7.11.0", - "chai": "^4.2.0", + "@commitlint/cli": "^17.3.0", + "@commitlint/config-conventional": "^17.3.0", + "@krakenjs/babel-config-grumbler": "^8.0.7", + "@krakenjs/eslint-config-grumbler": "^8.0.7", + "@krakenjs/typescript-config-grumbler": "^8.0.7", + "@krakenjs/webpack-config-grumbler": "^8.1.0-alpha.1", + "@vitest/coverage-c8": "^0.25.3", + "@vitest/ui": "^0.25.3", + "chai": "^4.3.7", "cross-env": "^7.0.3", - "flowgen": "^1.16.0", - "happy-dom": "^2.55.0", - "husky": "^7.0.4", + "flowgen": "^1.20.1", + "happy-dom": "^7.7.0", + "husky": "^8.0.2", "lint-staged": "^13.0.3", "prettier": "2.7.1", - "standard-version": "^9.3.2", - "ts-node": "^10.5.0", - "typescript": "4.6.3", - "vitest": "0.16.0" + "standard-version": "^9.5.0", + "ts-node": "^10.9.1", + "typescript": "4.9.3", + "vite": "^3.2.4", + "vitest": "0.25.3" }, "lint-staged": { "**/*": "prettier --write --ignore-unknown" diff --git a/src/util.ts b/src/util.ts index f3acde5..99902d1 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,7 +2,6 @@ export function isRegex(item: unknown): boolean { return Object.prototype.toString.call(item) === "[object RegExp]"; } -// eslint-disable-next-line @typescript-eslint/no-unused-vars export function noop(...args: ReadonlyArray): void { // pass } diff --git a/src/utils.ts b/src/utils.ts index e18a7ef..1d30ec4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,10 +1,8 @@ -/* eslint max-lines: 0, @typescript-eslint/prefer-optional-chain: 0 */ -/* global NodeJS */ import { isRegex, noop } from "./util"; -import type { - CrossDomainWindowType, - SameDomainWindowType, - DomainMatcher, +import { + type CrossDomainWindowType, + type SameDomainWindowType, + type DomainMatcher, } from "./types"; import { PROTOCOL, WILDCARD } from "./constants"; diff --git a/tsconfig.json b/tsconfig.json index a071093..5613994 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "@krakenjs/grumbler-scripts/config/tsconfig.json" + "extends": "@krakenjs/typescript-config-grumbler/tsconfig.json" } From 2e6ace7c87dee5022c456d87fb241ee25817828e Mon Sep 17 00:00:00 2001 From: Westin Wrzesinski Date: Tue, 22 Nov 2022 13:32:49 -0600 Subject: [PATCH 3/4] fix: tsc runs and oddly passes --- src/index.flow.js | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 src/index.flow.js diff --git a/src/index.flow.js b/src/index.flow.js deleted file mode 100644 index c59005e..0000000 --- a/src/index.flow.js +++ /dev/null @@ -1,2 +0,0 @@ -/* @flow */ -declare var __TEST__: boolean; From b25893c268c9da9808980dbf4bbeea5bf7886e9f Mon Sep 17 00:00:00 2001 From: Westin Wrzesinski Date: Tue, 22 Nov 2022 14:27:49 -0600 Subject: [PATCH 4/4] fix: tests pass --- .eslintrc.js | 10 +++ package.json | 4 +- src/types.ts | 4 +- src/util.ts | 3 +- src/utils.ts | 125 +++++++++++++++--------------- test/closeWindow.test.ts | 2 +- test/getAllFramesInWindow.test.ts | 12 ++- test/getDomain.test.ts | 5 +- test/getParent.test.ts | 2 +- test/getParents.test.ts | 2 +- test/isBlankDomain.test.ts | 7 +- test/isSameDomain.test.ts | 27 +++++-- test/utils.ts | 5 +- webpack.config.js | 40 ---------- 14 files changed, 117 insertions(+), 131 deletions(-) delete mode 100644 webpack.config.js diff --git a/.eslintrc.js b/.eslintrc.js index 84bcf1c..163f512 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,5 +5,15 @@ module.exports = { rules: { "keyword-spacing": "off", "@typescript-eslint/keyword-spacing": "off", + + // off for initial ts conversion + // Implicit any in catch clause + "@typescript-eslint/no-implicit-any-catch": "off", + // Prefer using an optional chain expression instead, as it's more concise and easier to read + "@typescript-eslint/prefer-optional-chain": "off", + // Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator + "@typescript-eslint/prefer-nullish-coalescing": "off", + // Generic Object Injection Sink + "security/detect-object-injection": "off", }, }; diff --git a/package.json b/package.json index b59ea13..13807ce 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,12 @@ "description": "Utilities for dealing with cross-domain windows.", "main": "dist/cross-domain-utils.js", "module": "dist/index.js", - "types": "dist/types/index.d.ts", + "types": "dist/esm/index.d.ts", "sideEffects": false, "scripts": { "build": "npm run test && npm run babel && npm run webpack && npm run build:types", "build:flow": "find ./dist -type f -not -path './node_modules/*' -name '*.d.ts' -exec sh -c 'flowgen --add-flow-header $1 -o ${1%.*.*}.js.flow' _ '{}' \\;", - "build:tsc": "tsc src/* --outDir ./dist/esm --declaration --emitDeclarationOnly", + "build:tsc": "tsc src/* --outDir ./dist/esm --declaration --emitDeclarationOnly --strict", "build:types": "npm run build:tsc && npm run build:flow", "webpack": "cross-env NODE_ENV=production babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --progress --output-path dist", "babel": "cross-env NODE_ENV=production babel src/ --out-dir ./dist/esm/ --extensions .ts,.tsx", diff --git a/src/types.ts b/src/types.ts index 92ae4e0..7bd05c9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,6 +7,6 @@ export type SameDomainWindowType = Omit< export type DomainMatcher = | string - | ReadonlyArray - | ReadonlyArray + | readonly string[] + | readonly string[] | RegExp; diff --git a/src/util.ts b/src/util.ts index 99902d1..d982f0a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,6 +2,7 @@ export function isRegex(item: unknown): boolean { return Object.prototype.toString.call(item) === "[object RegExp]"; } -export function noop(...args: ReadonlyArray): void { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function noop(...args: readonly unknown[]): void { // pass } diff --git a/src/utils.ts b/src/utils.ts index 1d30ec4..0869cb4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -13,9 +13,9 @@ export function getActualProtocol(win: SameDomainWindowType = window): string { } export function getProtocol(win: SameDomainWindowType = window): string { - // @ts-ignore mockDomain does not exist on window + // @ts-expect-error mockDomain does not exist on window if (win.mockDomain) { - // @ts-ignore mockDomain does not exist on window + // @ts-expect-error mockDomain does not exist on window // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call const protocol = win.mockDomain.split("//")[0]; @@ -42,7 +42,7 @@ export function isMockProtocol(win: SameDomainWindowType = window): boolean { export function getParent( win: CrossDomainWindowType = window -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { if (!win) { return; } @@ -58,7 +58,7 @@ export function getParent( export function getOpener( win: CrossDomainWindowType = window -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { if (!win) { return; } @@ -106,11 +106,10 @@ export function getActualDomain(win: SameDomainWindowType = window): string { } if (protocol === PROTOCOL.ABOUT) { - // @ts-ignore - trying to reassing to something that might be window + // @ts-expect-error - trying to reassing to something that might be window const parent = getParent(win); if (parent && canReadFromWindow(parent)) { - // @ts-ignore return getActualDomain(parent); } @@ -131,12 +130,12 @@ export function getDomain(win: SameDomainWindowType = window): string { if ( domain && - // @ts-ignore - mockDomain + // @ts-expect-error - mockDomain win.mockDomain && - // @ts-ignore - mockDomain - (win.mockDomain as string).indexOf(PROTOCOL.MOCK) === 0 + // @ts-expect-error - mockDomain + (win.mockDomain as string).startsWith(PROTOCOL.MOCK) ) { - // @ts-ignore - mockDomain + // @ts-expect-error - mockDomain return win.mockDomain as string; } @@ -245,7 +244,7 @@ export function assertSameDomain( export function getParents( win: CrossDomainWindowType -): ReadonlyArray { +): readonly CrossDomainWindowType[] { const result = []; try { @@ -274,17 +273,15 @@ export function isAncestorParent( return childParent === parent; } - if (getParents(child).indexOf(parent) !== -1) { + if (getParents(child).includes(parent)) { return true; } return false; } -export function getFrames( - win: CrossDomainWindowType -): Array { - const result: Array = []; +export function getFrames(win: CrossDomainWindowType): CrossDomainWindowType[] { + const result: CrossDomainWindowType[] = []; let frames; try { @@ -342,7 +339,7 @@ export function getFrames( export function getAllChildFrames( win: CrossDomainWindowType -): ReadonlyArray { +): readonly CrossDomainWindowType[] { const result = []; for (const frame of getFrames(win)) { @@ -358,7 +355,7 @@ export function getAllChildFrames( export function getTop( win: CrossDomainWindowType = window -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { try { if (win.top) { return win.top; @@ -404,25 +401,26 @@ export function getTop( export function getNextOpener( win: CrossDomainWindowType = window -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { return getOpener(getTop(win) || win); } export function getUltimateTop( win: CrossDomainWindowType = window -): CrossDomainWindowType | null { +): CrossDomainWindowType | undefined { const opener = getNextOpener(win); if (opener) { return getUltimateTop(opener); } + // @ts-expect-error where does top come from here? return top; } export function getAllFramesInWindow( win: CrossDomainWindowType -): ReadonlyArray { +): readonly CrossDomainWindowType[] { const top = getTop(win); if (!top) { @@ -432,7 +430,7 @@ export function getAllFramesInWindow( let result = [...getAllChildFrames(top), top]; // Win may be in shadow dom - if (result.indexOf(win) === -1) { + if (!result.includes(win)) { result = [...result, win, ...getAllChildFrames(win)]; } @@ -441,7 +439,7 @@ export function getAllFramesInWindow( export function getAllWindows( win: CrossDomainWindowType = window -): ReadonlyArray { +): readonly CrossDomainWindowType[] { const frames = getAllFramesInWindow(win); const opener = getNextOpener(win); @@ -471,11 +469,11 @@ export function isFrameWindowClosed(frame: HTMLIFrameElement): boolean { let parent = frame; while (parent.parentNode && parent.parentNode !== parent) { - // @ts-ignore + // @ts-expect-error parentNode is missing from HTMLIFrameElement parent = parent.parentNode; } - // @ts-ignore - host does not exist on type frame + // @ts-expect-error - host does not exist on type frame // eslint-disable-next-line @typescript-eslint/no-unsafe-argument if (!parent.host || !doc.documentElement.contains(parent.host)) { return true; @@ -485,7 +483,7 @@ export function isFrameWindowClosed(frame: HTMLIFrameElement): boolean { return false; } -function safeIndexOf(collection: ReadonlyArray, item: T): number { +function safeIndexOf(collection: readonly T[], item: T): number { for (let i = 0; i < collection.length; i++) { try { if (collection[i] === item) { @@ -499,8 +497,8 @@ function safeIndexOf(collection: ReadonlyArray, item: T): number { return -1; } -const iframeWindows: Array = []; -const iframeFrames: Array = []; +const iframeWindows: CrossDomainWindowType[] = []; +const iframeFrames: HTMLIFrameElement[] = []; export function isWindowClosed( win: CrossDomainWindowType, @@ -537,7 +535,7 @@ export function isWindowClosed( if (allowMock && isSameDomain(win)) { try { - // @ts-ignore + // @ts-expect-error win.mockclosed is our own added var on window if (win.mockclosed) { return true; } @@ -608,17 +606,17 @@ export function linkFrameWindow(frame: HTMLIFrameElement): void { } export function getUserAgent( - win: SameDomainWindowType | null | undefined + win: SameDomainWindowType | undefined | undefined ): string { win = win || window; - // @ts-ignore + // @ts-expect-error win.mockUserAgent is our own added var return (win.navigator.mockUserAgent as string) || win.navigator.userAgent; } export function getFrameByName( win: CrossDomainWindowType, name: string -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { const winFrames = getFrames(win); for (const childFrame of winFrames) { @@ -626,7 +624,7 @@ export function getFrameByName( if ( isSameDomain(childFrame) && childFrame.name === name && - winFrames.indexOf(childFrame) !== -1 + winFrames.includes(childFrame) ) { return childFrame; } @@ -636,10 +634,10 @@ export function getFrameByName( } try { - // @ts-ignore + // @ts-expect-error win.frames is implicit any so no indexing // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (winFrames.indexOf(win.frames[name]) !== -1) { - // @ts-ignore + if (winFrames.includes(win.frames[name])) { + // @ts-expect-error win.frames is implicit any so no indexing return win.frames[name] as CrossDomainWindowType; } } catch (err: unknown) { @@ -647,10 +645,10 @@ export function getFrameByName( } try { - // @ts-ignore + // @ts-expect-error win can't be indexed by name which is string // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (winFrames.indexOf(win[name]) !== -1) { - // @ts-ignore + if (winFrames.includes(win[name])) { + // @ts-expect-error win can't be indexed by name which is string return win[name] as CrossDomainWindowType; } } catch (err: unknown) { @@ -661,7 +659,7 @@ export function getFrameByName( export function findChildFrameByName( win: CrossDomainWindowType, name: string -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { const frame = getFrameByName(win, name); if (frame) { @@ -680,7 +678,7 @@ export function findChildFrameByName( export function findFrameByName( win: CrossDomainWindowType, name: string -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { const frame = getFrameByName(win, name); if (frame) { @@ -719,7 +717,7 @@ export function isOpener( export function getAncestor( win: CrossDomainWindowType = window -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { win = win || window; const opener = getOpener(win); @@ -736,12 +734,12 @@ export function getAncestor( export function getAncestors( win: CrossDomainWindowType -): ReadonlyArray { +): readonly CrossDomainWindowType[] { const results = []; let ancestor = win; while (ancestor) { - // @ts-ignore + // @ts-expect-error type Window | undefined is not assignable to type Window ancestor = getAncestor(ancestor); if (ancestor) { @@ -796,8 +794,8 @@ export function isFullpage(win: CrossDomainWindowType = window): boolean { } function anyMatch( - collection1: ReadonlyArray, - collection2: ReadonlyArray + collection1: readonly unknown[], + collection2: readonly unknown[] ): boolean { for (const item1 of collection1) { for (const item2 of collection2) { @@ -817,7 +815,7 @@ export function getDistanceFromTop( let parent = win; while (parent) { - // @ts-ignore - trying to reassing to something that might be window + // @ts-expect-error - trying to reassing to something that might be window parent = getParent(parent); if (parent) { @@ -831,7 +829,7 @@ export function getDistanceFromTop( export function getNthParent( win: CrossDomainWindowType, n = 1 -): CrossDomainWindowType | undefined | null { +): CrossDomainWindowType | undefined | undefined { let parent = win; for (let i = 0; i < n; i++) { @@ -839,7 +837,7 @@ export function getNthParent( return; } - // @ts-ignore - trying to reassign to something that might be window + // @ts-expect-error - trying to reassign to something that might be window parent = getParent(parent); } @@ -849,7 +847,7 @@ export function getNthParent( export function getNthParentFromTop( win: CrossDomainWindowType, n = 1 -): CrossDomainWindowType | null | undefined { +): CrossDomainWindowType | undefined | undefined { return getNthParent(win, getDistanceFromTop(win) - n); } @@ -920,7 +918,7 @@ export function matchDomain( return false; } - // @ts-ignore - earlier already shortcutted the string case + // @ts-expect-error - earlier already shortcutted the string case // eslint-disable-next-line @typescript-eslint/no-unsafe-call return Boolean(origin.match(pattern)); } @@ -955,7 +953,7 @@ export function stringifyDomainPattern(pattern: DomainMatcher): string { export function getDomainFromUrl(url: string): string { let domain; - if (url.match(/^(https?|mock|file):\/\//)) { + if (/^(https?|mock|file):\/\//.exec(url)) { domain = url; } else { return getDomain(); @@ -967,7 +965,7 @@ export function getDomainFromUrl(url: string): string { export function onCloseWindow( win: CrossDomainWindowType, - callback: (...args: Array) => void, + callback: (...args: unknown[]) => void, delay = 1000, maxtime = Infinity ): { cancel: () => void } { @@ -1035,7 +1033,7 @@ export function isWindow(obj: unknown): boolean { } try { - // @ts-ignore not complete sure that obj has self. needs guard + // @ts-expect-error not complete sure that obj has self. needs guard if (obj && obj.self === obj) { return true; } @@ -1046,7 +1044,7 @@ export function isWindow(obj: unknown): boolean { } try { - // @ts-ignore not complete sure that obj has parent. needs guard + // @ts-expect-error not complete sure that obj has parent. needs guard if (obj && obj.parent === obj) { return true; } @@ -1057,7 +1055,7 @@ export function isWindow(obj: unknown): boolean { } try { - // @ts-ignore not complete sure that obj has top. needs guard + // @ts-expect-error not complete sure that obj has top. needs guard if (obj && obj.top === obj) { return true; } @@ -1068,7 +1066,7 @@ export function isWindow(obj: unknown): boolean { } try { - // @ts-ignore this equality check is a self compare + // @ts-expect-error this equality check is a self compare // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression, no-self-compare if (noop(obj === obj) === "__unlikely_value__") { return false; @@ -1080,7 +1078,7 @@ export function isWindow(obj: unknown): boolean { try { if ( obj && - // @ts-ignore more shenanigans + // @ts-expect-error more shenanigans obj.__cross_domain_utils_window_check__ === "__unlikely_value__" ) { return false; @@ -1090,7 +1088,7 @@ export function isWindow(obj: unknown): boolean { } try { - // @ts-ignore needs guard + // @ts-expect-error needs guard if ("postMessage" in obj && "self" in obj && "location" in obj) { return true; } @@ -1116,7 +1114,7 @@ export function isCurrentDomain(domain: string): boolean { } export function isMockDomain(domain: string): boolean { - return domain.indexOf(PROTOCOL.MOCK) === 0; + return domain.startsWith(PROTOCOL.MOCK); } export function normalizeMockUrl(url: string): string { @@ -1124,7 +1122,7 @@ export function normalizeMockUrl(url: string): string { return url; } - // @ts-ignore - global + // @ts-expect-error - global if (!__TEST__) { throw new Error(`Mock urls not supported out of test mode`); } @@ -1134,12 +1132,13 @@ export function normalizeMockUrl(url: string): string { export function getFrameForWindow( win: CrossDomainWindowType -): HTMLElement | Element | null | undefined { +): HTMLElement | Element | undefined { if (isSameDomain(win)) { + // @ts-expect-error Type 'Element | null' is not assignable to type 'Element | HTMLElement | undefined' return assertSameDomain(win).frameElement; } - // @ts-ignore not an array type, but is iterable so fine + // @ts-expect-error not an array type, but is iterable so fine for (const frame of document.querySelectorAll("iframe")) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (frame && frame.contentWindow && frame.contentWindow === win) { diff --git a/test/closeWindow.test.ts b/test/closeWindow.test.ts index 53bcd0a..9ff546a 100644 --- a/test/closeWindow.test.ts +++ b/test/closeWindow.test.ts @@ -12,7 +12,7 @@ test("closeWindow will call window.close", () => { }, }); - // @ts-ignore + // @ts-expect-error Argument of type 'Window | SameDomainWindowType' is not assignable to parameter of type 'Window' closeWindow(win); assert(fnCalled, `Expected window.close to be called`); diff --git a/test/getAllFramesInWindow.test.ts b/test/getAllFramesInWindow.test.ts index f8eadf8..d58508c 100644 --- a/test/getAllFramesInWindow.test.ts +++ b/test/getAllFramesInWindow.test.ts @@ -33,7 +33,7 @@ test("getAllFramesInWindow should get all of the frames", () => { z.parent = z; const allFrames = [a, b, x, y, z]; - // @ts-ignore x is not type of window + // @ts-expect-error x is not type of window const foundFrames = getAllFramesInWindow(x); assert( @@ -42,24 +42,22 @@ test("getAllFramesInWindow should get all of the frames", () => { ); for (const frame of allFrames) { - // @ts-ignore frame is not type of window + // @ts-expect-error frame is not type of window + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions assert(foundFrames.includes(frame), `Did not find frame ${frame.name}`); } }); test("should get a mock frame defined in window.frames", () => { const frames = window.frames; - const mockFrame = {}; - // @ts-ignore - window.frames = [mockFrame]; + const mockFrame = {} as unknown as Window; + window.frames = [mockFrame] as unknown as Window; const foundFrames = getAllFramesInWindow(window); assert( - // @ts-ignore foundFrames.includes(mockFrame), `getAllFramesInWindow expected to find mock frame in window.frames` ); - // @ts-ignore window.frames = frames; }); diff --git a/test/getDomain.test.ts b/test/getDomain.test.ts index 079f051..144eec7 100644 --- a/test/getDomain.test.ts +++ b/test/getDomain.test.ts @@ -5,8 +5,9 @@ import { getDomain } from "../src"; import { getSameDomainWindow } from "./utils"; test("getDomain should get the domain for the current window", () => { - // @ts-ignore - window.location = new URL("https://www.paypal.com/sdk/js"); + window.location = new URL( + "https://www.paypal.com/sdk/js" + ) as unknown as Location; const domain = getDomain(); const expectedDomain = `${window.location.protocol}//${window.location.host}`; diff --git a/test/getParent.test.ts b/test/getParent.test.ts index 6bddf0b..e4ad0d0 100644 --- a/test/getParent.test.ts +++ b/test/getParent.test.ts @@ -15,7 +15,7 @@ test("getParent should get the parent window if there is one", () => { test("getParent should not get the parent window if the parent is the same window", () => { const win = getCrossDomainWindow({}); - // @ts-ignore + // @ts-expect-error window.parent is readonly win.parent = win; const parent = getParent(win); diff --git a/test/getParents.test.ts b/test/getParents.test.ts index f4582a2..6badc96 100644 --- a/test/getParents.test.ts +++ b/test/getParents.test.ts @@ -13,7 +13,7 @@ test("getParents should get all of a windows parents", () => { }, }); - // @ts-ignore + // @ts-expect-error window.parent is readonly win.parent.parent.parent.parent = win.parent.parent.parent; const parents = getParents(win); diff --git a/test/isBlankDomain.test.ts b/test/isBlankDomain.test.ts index f67562a..0507d15 100644 --- a/test/isBlankDomain.test.ts +++ b/test/isBlankDomain.test.ts @@ -13,7 +13,8 @@ test("isBlankDomain returns true if window.href is falsy", () => { }, }) ); - // @ts-ignore + + // @ts-expect-error Error Argument of type '(win: Window) => boolean' is not assignable to parameter of type '(value: Window | SameDomainWindowType const results = windows.map(isBlankDomain); const expectedResult = true; assert( @@ -29,7 +30,7 @@ test("isBlankDomain returns true if window.href about:blank", () => { }, }); const expectedResult = true; - // @ts-ignore + // @ts-expect-error Error Argument of type '(win: Window) => boolean' is not assignable to parameter of type '(value: Window | SameDomainWindowType const result = isBlankDomain(win); assert( result === expectedResult, @@ -44,7 +45,7 @@ test("isBlankDomain should return false if window.href is truthy but not about:b }, }); const expectedResult = false; - // @ts-ignore + // @ts-expect-error Error Argument of type '(win: Window) => boolean' is not assignable to parameter of type '(value: Window | SameDomainWindowType const result = isBlankDomain(win); assert( result === expectedResult, diff --git a/test/isSameDomain.test.ts b/test/isSameDomain.test.ts index 84a41ad..73d51a0 100644 --- a/test/isSameDomain.test.ts +++ b/test/isSameDomain.test.ts @@ -5,8 +5,9 @@ import { isSameDomain } from "../src"; import { getSameDomainWindow } from "./utils"; test("isSameDomain should give a positive result for isSameDomain", () => { - // @ts-ignore - window.location = new URL("http://www.paypal.com/sdk/js"); + window.location = new URL( + "http://www.paypal.com/sdk/js" + ) as unknown as Location; const win = getSameDomainWindow({ location: { @@ -37,6 +38,7 @@ test("isSameDomain should give a negative result for isSameDomain with a differe return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; @@ -59,6 +61,7 @@ test("isSameDomain should give a negative result for isSameDomain with a differe return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; @@ -75,11 +78,13 @@ test("isSameDomain should give a negative result for isSameDomain with a differe host: "foobar.com:12345", }, }); + Object.defineProperty(win.location, "href", { get(): string { return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; @@ -99,11 +104,13 @@ test("isSameDomain should give a negative result for isSameDomain when an error host: window.location.host, }, }); + Object.defineProperty(win.location, "href", { get(): string { return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; @@ -123,11 +130,13 @@ test("isSameDomain should give a negative result for isSameDomain when an error }, }, }); + Object.defineProperty(win.location, "href", { get(): string { return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; @@ -139,6 +148,7 @@ test("isSameDomain should give a negative result for isSameDomain when an error test("isSameDomain should give a negative result for isSameDomain when location is non-enumerable", () => { const win = getSameDomainWindow({}); + Object.defineProperty(win, "location", { value: { protocol: window.location.protocol, @@ -146,11 +156,13 @@ test("isSameDomain should give a negative result for isSameDomain when location }, enumerable: false, }); + Object.defineProperty(win.location, "href", { get(): string { return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; @@ -161,10 +173,8 @@ test("isSameDomain should give a negative result for isSameDomain when location }); test("isSameDomain should give a positive result for isSameDomain when mockDomain matches", () => { - // @ts-ignore + // @ts-expect-error win.mockDomain is our own var window.mockDomain = "mock://foobar.com:12345"; - // @ts-ignore - window.location = new URL("mock://foobar.com:12345"); const win = getSameDomainWindow({ location: { @@ -173,11 +183,13 @@ test("isSameDomain should give a positive result for isSameDomain when mockDomai }, mockDomain: "mock://foobar.com:12345", }); + Object.defineProperty(win.location, "href", { get(): string { return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = true; @@ -188,8 +200,9 @@ test("isSameDomain should give a positive result for isSameDomain when mockDomai }); test("isSameDomain should give a negative result for isSameDomain when mockDomain does not match", () => { - // @ts-ignore + // @ts-expect-error win.mockDomain is our own var window.mockDomain = "mock://fizzbuzz.com:345"; + const win = getSameDomainWindow({ location: { protocol: window.location.protocol, @@ -197,11 +210,13 @@ test("isSameDomain should give a negative result for isSameDomain when mockDomai }, mockDomain: "mock://foobar.com:12345", }); + Object.defineProperty(win.location, "href", { get(): string { return `${win.location.protocol}//${win.location.host}`; }, }); + const result = isSameDomain(win); const expectedResult = false; diff --git a/test/utils.ts b/test/utils.ts index ae95432..8f68697 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -3,15 +3,16 @@ import type { CrossDomainWindowType, SameDomainWindowType } from "../src/types"; export function getCrossDomainWindow( options: Record ): CrossDomainWindowType { - // @ts-ignore not a true window type + // @ts-expect-error not a true window type return { ...options, }; } + export function getSameDomainWindow( options: Record ): CrossDomainWindowType | SameDomainWindowType | Window { - // @ts-ignore not a true window type + // @ts-expect-error not a true window type return { ...options, }; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 71293d6..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,40 +0,0 @@ -/* @flow */ -/* eslint import/no-nodejs-modules: off */ - -import type { WebpackConfig } from "@krakenjs/webpack-config-grumbler/index.flow"; -import { getWebpackConfig } from "@krakenjs/webpack-config-grumbler"; - -let FILE_NAME = "cross-domain-utils"; -let MODULE_NAME = "crossDomainUtils"; - -export let WEBPACK_CONFIG: WebpackConfig = getWebpackConfig({ - filename: `${FILE_NAME}.js`, - modulename: MODULE_NAME, - minify: false, - vars: { - __MIN__: false, - __TEST__: false, - }, -}); - -export let WEBPACK_CONFIG_MIN: WebpackConfig = getWebpackConfig({ - filename: `${FILE_NAME}.min.js`, - modulename: MODULE_NAME, - minify: true, - vars: { - __MIN__: true, - __TEST__: false, - }, -}); - -export let WEBPACK_CONFIG_TEST: WebpackConfig = getWebpackConfig({ - modulename: MODULE_NAME, - options: { - devtool: "inline-source-map", - }, - vars: { - __TEST__: true, - }, -}); - -export default [WEBPACK_CONFIG, WEBPACK_CONFIG_MIN];