Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## v1.12.9

* fix(admob): remove the policy-prohibited interstitial shown while exiting the app

## v1.12.8

* fix(terminal): close tab on process end and speed up long-press select by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/2587
Expand Down
2 changes: 1 addition & 1 deletion config.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.foxdebug.acode" android-versionCode="1004" version="1.12.8"
<widget id="com.foxdebug.acode" android-versionCode="1005" version="1.12.9"
xmlns="http://www.w3.org/ns/widgets"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cdv="http://cordova.apache.org/ns/1.0">
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "com.foxdebug.acode",
"displayName": "Acode",
"version": "1.12.8",
"version": "1.12.9",
"description": "Acode is a code editor for android",
"scripts": {
"lang": "node ./utils/lang.js",
Expand Down
3 changes: 0 additions & 3 deletions src/lib/actionStack.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import confirm from "dialogs/confirm";
import appSettings from "lib/settings";
import helpers from "utils/helpers";

const stack = [];
let mark = null;
Expand Down Expand Up @@ -94,8 +93,6 @@ export default {
}
}

helpers.showInterstitialIfReady();

exitApp();
}
},
Expand Down
126 changes: 126 additions & 0 deletions tests/unit/actionStack.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
confirm: vi.fn(),
exitApp: vi.fn(),
settings: {
value: {
confirmOnExit: false,
},
},
showInterstitialIfReady: vi.fn(),
}));

vi.mock("dialogs/confirm", () => ({
default: mocks.confirm,
}));

vi.mock("lib/settings", () => ({
default: mocks.settings,
}));

// Keep this mock as a policy regression guard. If the exit flow starts importing
// the ad helper again, the assertions below will catch the prohibited placement.
vi.mock("utils/helpers", () => ({
default: {
showInterstitialIfReady: mocks.showInterstitialIfReady,
},
}));

import actionStack from "lib/actionStack";

const originalNavigatorDescriptor = Object.getOwnPropertyDescriptor(
globalThis,
"navigator",
);
const originalAcode = globalThis.acode;
const originalStrings = globalThis.strings;

function setNavigator() {
Object.defineProperty(globalThis, "navigator", {
configurable: true,
value: {
app: {
exitApp: mocks.exitApp,
},
},
});
}

describe("actionStack app exit", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.settings.value.confirmOnExit = false;
actionStack.onCloseApp = undefined;
actionStack.unfreeze();
globalThis.acode = { exitAppMessage: "Close Acode?" };
globalThis.strings = { warning: "Warning" };
setNavigator();
});

afterAll(() => {
actionStack.onCloseApp = undefined;
globalThis.acode = originalAcode;
globalThis.strings = originalStrings;

if (originalNavigatorDescriptor) {
Object.defineProperty(
globalThis,
"navigator",
originalNavigatorDescriptor,
);
} else {
Reflect.deleteProperty(globalThis, "navigator");
}
});

it("exits without showing an interstitial after confirmation", async () => {
mocks.settings.value.confirmOnExit = true;
mocks.confirm.mockResolvedValue(true);

await actionStack.pop();

expect(mocks.confirm).toHaveBeenCalledWith("WARNING", "Close Acode?");
expect(mocks.showInterstitialIfReady).not.toHaveBeenCalled();
expect(mocks.exitApp).toHaveBeenCalledOnce();
});

it("does not show an interstitial or exit when confirmation is cancelled", async () => {
mocks.settings.value.confirmOnExit = true;
mocks.confirm.mockResolvedValue(false);

await actionStack.pop();

expect(mocks.showInterstitialIfReady).not.toHaveBeenCalled();
expect(mocks.exitApp).not.toHaveBeenCalled();
});

it("runs a synchronous close callback before exiting", async () => {
const onClose = vi.fn();
actionStack.onCloseApp = onClose;

await actionStack.pop();

expect(onClose).toHaveBeenCalledOnce();
expect(mocks.showInterstitialIfReady).not.toHaveBeenCalled();
expect(mocks.exitApp).toHaveBeenCalledOnce();
});

it("waits for an asynchronous close callback before exiting", async () => {
let finishClose;
const closeComplete = new Promise((resolve) => {
finishClose = resolve;
});
actionStack.onCloseApp = vi.fn(() => closeComplete);

await actionStack.pop();

expect(mocks.exitApp).not.toHaveBeenCalled();
finishClose();
await closeComplete;
await Promise.resolve();

expect(mocks.showInterstitialIfReady).not.toHaveBeenCalled();
expect(mocks.exitApp).toHaveBeenCalledOnce();
});
});