Skip to content

Instantly share code, notes, and snippets.

@RomkeVdMeulen
Last active August 27, 2019 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RomkeVdMeulen/214232ef7b61a2c514787d45d6f5aca4 to your computer and use it in GitHub Desktop.
Save RomkeVdMeulen/214232ef7b61a2c514787d45d6f5aca4 to your computer and use it in GitHub Desktop.
Decorator for Aurelia routes that ask the user to confirm when unloading with unsaved changes
import {decorateMethod} from "util/objects";
export interface ConfirmUnloadConfig {
hasUnsavedChanges(): boolean;
getUnloadConfirmMessage(): string;
}
export function confirmUnload(viewModel: any): any {
decorateMethod(viewModel, "attached", function attached() {
const self: ConfirmUnloadConfig = this;
viewModel.unloadListener = (event: Event) => {
if (self.hasUnsavedChanges()) {
event.preventDefault();
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#Examples
(<any> event).returnValue = "";
}
};
confirmUnload.window.addEventListener("beforeunload", viewModel.unloadListener);
});
decorateMethod(viewModel, "detached", function detached() {
confirmUnload.window.removeEventListener("beforeunload", viewModel.unloadListener);
});
decorateMethod(viewModel, "canDeactivate", function canDeactivate() {
const decoratedResult: boolean = arguments[arguments.length - 1];
if (decoratedResult === false) {
return false;
}
if (!this.hasUnsavedChanges()) {
return true;
}
return confirmUnload.window.confirm(this.getUnloadConfirmMessage());
});
}
confirmUnload.window = window;
import {expect} from "chai";
import {confirmUnload, ConfirmUnloadConfig} from "util/unload";
describe("@confirmUnload", () => {
let confirmMessage: string, confirmResult: boolean, unloadListener: any,
unloadListenerCleared = false;
beforeEach(() => {
confirmUnload.window = <any> {
addEventListener(event: string, listener: any) {
if (event !== "beforeunload") {
throw new Error("Unexpected call");
}
unloadListener = listener;
},
removeEventListener(event: string, listener: any) {
if (event !== "beforeunload" || listener !== unloadListener) {
throw new Error("Unexpected call");
}
unloadListenerCleared = true;
},
confirm(tekst: string) {
confirmMessage = tekst;
return confirmResult;
},
};
});
it("ensures that the user is asked for confirmation before navigating with unsaved changes in the VM", async () => {
let unsavedChanges = false, originalCanDeactivate = true;
const called = {
attached: false,
detached: false,
canDeactivate: false,
};
@confirmUnload
class DummyVM implements ConfirmUnloadConfig {
attached() {
called.attached = true;
}
detached() {
called.detached = true;
}
canDeactivate() {
called.canDeactivate = true;
return originalCanDeactivate;
}
hasUnsavedChanges(): boolean {
return unsavedChanges;
}
getUnloadConfirmMessage(): string {
return "Confirm Message";
}
}
expect(called.attached).to.be.false;
expect(called.detached).to.be.false;
expect(called.canDeactivate).to.be.false;
expect(unloadListener).not.to.exist;
expect(unloadListenerCleared).to.be.false;
const vmInstance = new DummyVM();
vmInstance.attached();
expect(called.attached).to.be.true;
expect(called.detached).to.be.false;
expect(unloadListener).to.exist;
const mockEvent = {
defaultPrevented: false,
preventDefault() {
this.defaultPrevented = true;
},
};
unloadListener(mockEvent);
expect(mockEvent.defaultPrevented).to.be.false;
confirmResult = false;
expect(vmInstance.canDeactivate()).to.be.true;
expect(called.canDeactivate).to.be.true;
expect(confirmMessage).not.to.exist;
originalCanDeactivate = false;
expect(vmInstance.canDeactivate()).to.be.false;
originalCanDeactivate = true;
unsavedChanges = true;
unloadListener(mockEvent);
expect(mockEvent.defaultPrevented).to.be.true;
expect(vmInstance.canDeactivate()).to.be.false;
expect(confirmMessage).to.equal("Confirm Message");
confirmResult = true;
expect(vmInstance.canDeactivate()).to.be.true;
expect(called.detached).to.be.false;
expect(unloadListenerCleared).to.be.false;
vmInstance.detached();
expect(called.detached).to.be.true;
expect(unloadListenerCleared).to.be.true;
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment