Skip to content

Commit

Permalink
fix: avoid throwing errors if container is no longer in page (#424)
Browse files Browse the repository at this point in the history
* fix: avoid throwing errors if container is no longer in page

* chore: added constants for repeated errors and additional tests

* chore: flow issue

* chore: renamed error and increased delay

* fix: additional check before throwing iframe error and remove unecessary test

* chore: removing additional iframe check

* chore: renamed render variable

* chore: removed isRendered variable
  • Loading branch information
oscarleonnogales committed Apr 24, 2023
1 parent 3c6fa18 commit e2825bb
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 32 deletions.
9 changes: 9 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ export const POST_MESSAGE = {
ALLOW_DELEGATE: `${ZOID}_allow_delegate`,
};

export const COMPONENT_ERROR = {
NAVIGATED_AWAY: "Window navigated away",
COMPONENT_DESTROYED: "Component destroyed",
COMPONENT_CLOSED: "Component closed",
WINDOW_CLOSED: "Detected component window close",
POPUP_CLOSE: "Detected popup close",
IFRAME_CLOSE: "Detected iframe close",
};

export const PROP_TYPE = {
STRING: ("string": "string"),
OBJECT: ("object": "object"),
Expand Down
19 changes: 14 additions & 5 deletions src/parent/parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
METHOD,
WINDOW_REFERENCE,
DEFAULT_DIMENSIONS,
COMPONENT_ERROR,
} from "../constants";
import {
getGlobal,
Expand Down Expand Up @@ -729,7 +730,15 @@ export function parentComponent<P, X, C>({
return clean.all(err);
})
.then(() => {
initPromise.asyncReject(err || new Error("Component destroyed"));
const error = err || new Error(COMPONENT_ERROR.COMPONENT_DESTROYED);
if (
(currentContainer && isElementClosed(currentContainer)) ||
error.message === COMPONENT_ERROR.NAVIGATED_AWAY
) {
initPromise.resolve();
} else {
initPromise.asyncReject(error);
}
});
};

Expand All @@ -749,7 +758,7 @@ export function parentComponent<P, X, C>({
return ZalgoPromise.try(() => {
return event.trigger(EVENT.CLOSE);
}).then(() => {
return destroy(err || new Error(`Component closed`));
return destroy(err || new Error(COMPONENT_ERROR.COMPONENT_CLOSED));
});
});
});
Expand Down Expand Up @@ -823,7 +832,7 @@ export function parentComponent<P, X, C>({
window,
"unload",
once(() => {
destroy(new Error(`Window navigated away`));
destroy(new Error(COMPONENT_ERROR.NAVIGATED_AWAY));
})
);

Expand Down Expand Up @@ -870,15 +879,15 @@ export function parentComponent<P, X, C>({
.then((isClosed) => {
if (isClosed) {
closed = true;
return close(new Error(`Detected component window close`));
return close(new Error(COMPONENT_ERROR.WINDOW_CLOSED));
}

return ZalgoPromise.delay(200)
.then(() => proxyWin.isClosed())
.then((secondIsClosed) => {
if (secondIsClosed) {
closed = true;
return close(new Error(`Detected component window close`));
return close(new Error(COMPONENT_ERROR.WINDOW_CLOSED));
}
});
})
Expand Down
31 changes: 4 additions & 27 deletions test/tests/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,9 @@ describe("zoid error cases", () => {
);
});

it("should call onclose when an iframe is closed by someone other than zoid during render", () => {
it("should error out when iframe is closed by someone other than zoid during render", () => {
return wrapPromise(
({ expect, avoid }) => {
({ expect }) => {
window.__component__ = () => {
return zoid.create({
tag: "test-onclose-iframe-closed-during-render",
Expand All @@ -343,17 +343,13 @@ describe("zoid error cases", () => {

onWindowOpen().then(
expect("onWindowOpen", ({ win: openedWindow }) => {
setTimeout(() => {
destroyElement(openedWindow.frameElement);
}, 1);
destroyElement(openedWindow.frameElement);
})
);

const component = window.__component__();
return component({
onClose: expect("onClose"),
onError: avoid("onError"),
onDestroy: expect("onDestroy"),
onError: expect("onError"),
})
.render("body", zoid.CONTEXT.IFRAME)
.catch(expect("catch"));
Expand Down Expand Up @@ -590,25 +586,6 @@ describe("zoid error cases", () => {
});
});

it("should error out where the domain is an invalid regex", () => {
return wrapPromise(({ expect }) => {
window.__component__ = () => {
return zoid.create({
tag: "test-render-domain-invalid-regex",
url: "mock://www.child.com/base/test/windows/child/index.htm",
domain: /^mock:\/\/www\.meep\.com$/,
});
};

const component = window.__component__();
return component({
onDestroy: expect("onDestroy"),
})
.render(getBody())
.catch(expect("catch"));
});
});

it("should error out where an invalid element is passed", () => {
return wrapPromise(({ expect }) => {
window.__component__ = () => {
Expand Down
59 changes: 59 additions & 0 deletions test/tests/remove.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,63 @@ describe("zoid remove cases", () => {
});
});
});

it("should not throw an error when parent container is removed before render completes", () => {
return wrapPromise(() => {
const { container } = getContainer();

window.__component__ = () => {
return zoid.create({
tag: "test-remove-before-render-complete",
url: () => "mock://www.child.com/base/test/windows/child/index.htm",
domain: "mock://www.child.com",
});
};

const component = window.__component__();
const onRenderedPromise = new ZalgoPromise();
const onClosePromise = new ZalgoPromise();

const renderPromise = component({
onRendered: () => onRenderedPromise.resolve(),
onClose: () => onClosePromise.resolve(),
}).render(container);

// manually remove before render completes
container.remove();

return renderPromise.then(() => {
ZalgoPromise.delay(5);
});
});
});

it("should not throw an error when window navigates away before render completes", () => {
return wrapPromise(() => {
const { container } = getContainer();

window.__component__ = () => {
return zoid.create({
tag: "test-navigation-before-render-complete",
url: () => "mock://www.child.com/base/test/windows/child/index.htm",
domain: "mock://www.child.com",
});
};

const component = window.__component__();
const onRenderedPromise = new ZalgoPromise();
const onClosePromise = new ZalgoPromise();

const renderPromise = component({
onRendered: () => onRenderedPromise.resolve(),
onClose: () => onClosePromise.resolve(),
}).render(container);

// manually reload window before render completes
window.dispatchEvent(new Event("unload"));
return renderPromise.then(() => {
ZalgoPromise.delay(5);
});
});
});
});

0 comments on commit e2825bb

Please sign in to comment.