diff --git a/src/index.html b/src/index.html
index 136ec1f456..a804594e2d 100644
--- a/src/index.html
+++ b/src/index.html
@@ -173,8 +173,10 @@
// this means that we are loaded in an iframe in the specrunner. Electron doesnt expose APIs
// in its iframes, so we directly use the top most windows electron api objects for tests to run properly.
console.warn("Phoenix is loaded in iframe, attempting to use electron APIs from window.top");
+ window.__ELECTRON__ = true;
window.electronAppAPI = window.top.window.electronAppAPI;
window.electronFSAPI = window.top.window.electronFSAPI;
+ window.electronAPI = window.top.window.electronAPI;
}
if(window.__TAURI__ || window.__ELECTRON__) {
window.__IS_NATIVE_SHELL__ = true;
diff --git a/src/phoenix/shell.js b/src/phoenix/shell.js
index f6796f5122..c9e3b4ed0c 100644
--- a/src/phoenix/shell.js
+++ b/src/phoenix/shell.js
@@ -66,6 +66,25 @@ async function _getTauriWindowLabel(prefix) {
throw new Error("Could not get a free window label to create tauri window");
}
+/**
+ * Opens a URL in a new Phoenix window. Works across all platforms (Tauri, Electron, browser).
+ *
+ * @param {string} url - The URL to open in the new window
+ * @param {Object} [options] - Window configuration options
+ * @param {string} [options.windowTitle] - Title for the window (defaults to label or URL)
+ * @param {boolean} [options.fullscreen] - Whether to open in fullscreen mode
+ * @param {boolean} [options.resizable=true] - Whether the window is resizable
+ * @param {number} [options.height=900] - Window height in pixels
+ * @param {number} [options.minHeight=600] - Minimum window height in pixels
+ * @param {number} [options.width=1366] - Window width in pixels
+ * @param {number} [options.minWidth=800] - Minimum window width in pixels
+ * @param {boolean} [options.acceptFirstMouse=true] - (Tauri only) Accept first mouse click
+ * @param {boolean} [options.preferTabs] - (Browser only) Prefer opening in a new tab
+ * @param {string} [options._prefixPvt] - Internal: window label prefix
+ * @returns {Promise<{label: string, isNativeWindow: boolean}>} Window object with `label` and `isNativeWindow` properties.
+ * - In Tauri/Electron: `{ label: string, isNativeWindow: true }` (Tauri returns WebviewWindow instance with these props)
+ * - In browser: Returns window.open() result with `isNativeWindow: false`
+ */
async function openURLInPhoenixWindow(url, {
windowTitle, fullscreen, resizable,
height, minHeight, width, minWidth, acceptFirstMouse, preferTabs, _prefixPvt = PHOENIX_EXTENSION_WINDOW_PREFIX
@@ -242,6 +261,27 @@ Phoenix.app = {
return window.electronAPI.focusWindow();
}
},
+ /**
+ * Closes a window by its label. Returns true if window was found and closed, false otherwise.
+ * @param {string} label - The window label to close
+ * @return {Promise}
+ */
+ closeWindowByLabel: async function (label) {
+ if(!Phoenix.isNativeApp){
+ throw new Error("closeWindowByLabel is not supported in browsers");
+ }
+ if(window.__TAURI__){
+ const win = window.__TAURI__.window.WebviewWindow.getByLabel(label);
+ if(win){
+ await win.close();
+ return true;
+ }
+ return false;
+ } else if(window.__ELECTRON__){
+ return window.electronAPI.closeWindowByLabel(label);
+ }
+ return false;
+ },
/**
* Gets the commandline argument in desktop builds and null in browser builds.
* Will always return CLI of the current process only.
@@ -689,7 +729,72 @@ Phoenix.app = {
getTimeSinceStartup: function () {
return Date.now() - Phoenix.startTime; // milliseconds elapsed since app start
},
- language: navigator.language
+ language: navigator.language,
+ /**
+ * Broadcast an event to all windows (excludes sender).
+ * @param {string} eventName - Name of the event
+ * @param {*} payload - Event data
+ * @returns {Promise}
+ */
+ emitToAllWindows: async function (eventName, payload) {
+ if (!Phoenix.isNativeApp) {
+ throw new Error("emitToAllWindows is not supported in browsers");
+ }
+ if (window.__TAURI__) {
+ return window.__TAURI__.event.emit(eventName, payload);
+ }
+ if (window.__ELECTRON__) {
+ return window.electronAPI.emitToAllWindows(eventName, payload);
+ }
+ },
+ /**
+ * Send an event to a specific window by label.
+ * @param {string} targetLabel - Window label to send to
+ * @param {string} eventName - Name of the event
+ * @param {*} payload - Event data
+ * @returns {Promise} True if window found and event sent
+ */
+ emitToWindow: async function (targetLabel, eventName, payload) {
+ if (!Phoenix.isNativeApp) {
+ throw new Error("emitToWindow is not supported in browsers");
+ }
+ if (window.__TAURI__) {
+ // Tauri doesn't have direct window-to-window emit, use global emit
+ // The listener filters by source if needed
+ return window.__TAURI__.event.emit(eventName, payload);
+ }
+ if (window.__ELECTRON__) {
+ return window.electronAPI.emitToWindow(targetLabel, eventName, payload);
+ }
+ return false;
+ },
+ /**
+ * Listen for events from other windows.
+ * @param {string} eventName - Name of the event to listen for
+ * @param {Function} callback - Called with (payload) when event received
+ * @returns {Function} Unlisten function to remove the listener
+ */
+ onWindowEvent: function (eventName, callback) {
+ if (!Phoenix.isNativeApp) {
+ throw new Error("onWindowEvent is not supported in browsers");
+ }
+ if (window.__TAURI__) {
+ let unlisten = null;
+ window.__TAURI__.event.listen(eventName, (event) => {
+ callback(event.payload);
+ }).then(fn => { unlisten = fn; });
+ // Return a function that will unlisten when called
+ return () => {
+ if (unlisten) {
+ unlisten();
+ }
+ };
+ }
+ if (window.__ELECTRON__) {
+ return window.electronAPI.onWindowEvent(eventName, callback);
+ }
+ return () => {}; // No-op for unsupported platforms
+ }
};
if(!window.appshell){
diff --git a/test/SpecRunner.html b/test/SpecRunner.html
index 9d910e9beb..1665f48c24 100644
--- a/test/SpecRunner.html
+++ b/test/SpecRunner.html
@@ -207,6 +207,17 @@
});
}
setupElectronBootVars();
+ // F12 to toggle dev tools in Electron
+ document.addEventListener('keydown', function(e) {
+ if (e.key === 'F12') {
+ e.preventDefault();
+ window.electronAPI.toggleDevTools();
+ }
+ if (e.key === 'F5') {
+ e.preventDefault();
+ location.reload();
+ }
+ });
}
diff --git a/test/SpecRunner.js b/test/SpecRunner.js
index 2fa1677ed1..0c17e0ace0 100644
--- a/test/SpecRunner.js
+++ b/test/SpecRunner.js
@@ -44,7 +44,8 @@ require.config({
"thirdparty/preact": "preact-compat",
"thirdparty/preact-test-utils": "preact-test-utils"
}
- }
+ },
+ locale: "en" // force English (US) for consistent test strings
});
window.logger = {
diff --git a/test/UnitTestReporter.js b/test/UnitTestReporter.js
index 995b623066..9894b543d4 100644
--- a/test/UnitTestReporter.js
+++ b/test/UnitTestReporter.js
@@ -65,18 +65,38 @@ define(function (require, exports, module) {
return '';
}
+ function hasCliFlag(args, flagName, shortFlag) {
+ return args.some(arg =>
+ arg === `--${flagName}` ||
+ arg.startsWith(`--${flagName}=`) ||
+ (shortFlag && arg === `-${shortFlag}`)
+ );
+ }
+
function quitIfNeeded(exitStatus) {
- if(!window.__TAURI__){
+ const isTauri = !!window.__TAURI__;
+ const isElectron = !!window.electronAppAPI?.isElectron;
+
+ if (!isTauri && !isElectron) {
return;
}
+
const WAIT_TIME_TO_COMPLETE_TEST_LOGGING_SEC = 10;
console.log("Scheduled Quit in Seconds: ", WAIT_TIME_TO_COMPLETE_TEST_LOGGING_SEC);
- setTimeout(()=>{
- window.__TAURI__.cli.getMatches().then(matches=>{
- if(matches && matches.args["quit-when-done"] && matches.args["quit-when-done"].occurrences) {
- window.__TAURI__.process.exit(exitStatus);
- }
- });
+ setTimeout(() => {
+ if (isTauri) {
+ window.__TAURI__.cli.getMatches().then(matches => {
+ if (matches && matches.args["quit-when-done"] && matches.args["quit-when-done"].occurrences) {
+ window.__TAURI__.process.exit(exitStatus);
+ }
+ });
+ } else if (isElectron) {
+ window.electronAppAPI.getCliArgs().then(args => {
+ if (hasCliFlag(args, 'quit-when-done', 'q')) {
+ window.electronAppAPI.quitApp(exitStatus);
+ }
+ });
+ }
}, WAIT_TIME_TO_COMPLETE_TEST_LOGGING_SEC * 1000);
}
diff --git a/test/UnitTestSuite.js b/test/UnitTestSuite.js
index e3f9a3e524..f2dd6c5905 100644
--- a/test/UnitTestSuite.js
+++ b/test/UnitTestSuite.js
@@ -22,7 +22,7 @@
define(function (require, exports, module) {
require("spec/Phoenix-platform-test");
- require("spec/Tauri-platform-test");
+ require("test/spec/Native-platform-test");
require("spec/trust-ring-test");
require("spec/utframework-suite-test");
require("spec/Async-test");
diff --git a/test/index-dist-test.html b/test/index-dist-test.html
index b0390820bb..63dbe9ba79 100644
--- a/test/index-dist-test.html
+++ b/test/index-dist-test.html
@@ -4,20 +4,48 @@
Starting tests...
diff --git a/test/spec/ExtensionInstallation-test.js b/test/spec/ExtensionInstallation-test.js
index 8c91e42ca9..b8d2120ba8 100644
--- a/test/spec/ExtensionInstallation-test.js
+++ b/test/spec/ExtensionInstallation-test.js
@@ -31,7 +31,8 @@ define(function (require, exports, module) {
const testFilePath = SpecRunnerUtils.getTestPath("/spec/extension-test-files");
- const tempDirectory = window.__TAURI__ ? Phoenix.VFS.getTauriAssetServeDir() + "tests": SpecRunnerUtils.getTempDirectory();
+ const tempDirectory = Phoenix.isNativeApp ?
+ Phoenix.VFS.getTauriAssetServeDir() + "tests": SpecRunnerUtils.getTempDirectory();
const extensionsRoot = tempDirectory + "/extensions";
const basicValidSrc = testFilePath + "/basic-valid-extension.zip",
@@ -79,7 +80,7 @@ define(function (require, exports, module) {
beforeAll(async function () {
await SpecRunnerUtils.ensureExistsDirAsync(tempDirectory);
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
basicValid = tempDirectory + "/basic-valid-extension.zip";
missingNameVersion = tempDirectory + "/missing-name-version.zip";
missingNameVersion = tempDirectory + "/missing-name-version.zip";
diff --git a/test/spec/ExtensionLoader-test.js b/test/spec/ExtensionLoader-test.js
index 0b06581e3d..291b3efedc 100644
--- a/test/spec/ExtensionLoader-test.js
+++ b/test/spec/ExtensionLoader-test.js
@@ -32,7 +32,7 @@ define(function (require, exports, module) {
SpecRunnerUtils = require("spec/SpecRunnerUtils");
const testPathSrc = SpecRunnerUtils.getTestPath("/spec/ExtensionLoader-test-files");
- const testPath = window.__TAURI__ ? Phoenix.VFS.getTauriAssetServeDir() + "tests": SpecRunnerUtils.getTempDirectory();
+ const testPath = Phoenix.isNativeApp ? Phoenix.VFS.getTauriAssetServeDir() + "tests": SpecRunnerUtils.getTempDirectory();
describe("ExtensionLoader", function () {
diff --git a/test/spec/ExtensionManager-test.js b/test/spec/ExtensionManager-test.js
index 722982895a..2571c33285 100644
--- a/test/spec/ExtensionManager-test.js
+++ b/test/spec/ExtensionManager-test.js
@@ -49,7 +49,7 @@ define(function (require, exports, module) {
mockExtensionList = require("text!spec/ExtensionManager-test-files/mockExtensionList.json"),
mockRegistry;
- const testPath = window.__TAURI__ ?
+ const testPath = Phoenix.isNativeApp ?
Phoenix.VFS.getTauriAssetServeDir() + "tests" :
SpecRunnerUtils.getTempDirectory();
const testSrc = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
@@ -808,6 +808,12 @@ define(function (require, exports, module) {
});
});
+ afterEach(function () {
+ // Clean up any lingering dialogs
+ Dialogs.cancelModalDialogIfOpen("install-extension-dialog");
+ $(".modal-wrapper").remove();
+ });
+
it("should set flag to keep local files for new installs", async function () {
var filename = "/path/to/downloaded/file.zip",
file = FileSystem.getFileForPath(filename),
diff --git a/test/spec/LowLevelFileIO-test.js b/test/spec/LowLevelFileIO-test.js
index 5fe7063fd2..3ec05e1cfc 100644
--- a/test/spec/LowLevelFileIO-test.js
+++ b/test/spec/LowLevelFileIO-test.js
@@ -797,7 +797,7 @@ define(function (require, exports, module) {
describe("specialDirectories", function () {
it("should have an application support directory", async function () {
// these tests are here as these are absolute unchanging dir convention used by phoenix.
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
const appSupportDIR = window.fs.getTauriVirtualPath(window._tauriBootVars.appLocalDir);
expect(brackets.app.getApplicationSupportDirectory().startsWith("/tauri/")).toBeTrue();
expect(brackets.app.getApplicationSupportDirectory()).toBe(appSupportDIR);
@@ -807,7 +807,7 @@ define(function (require, exports, module) {
});
it("should have a user documents directory", function () {
// these tests are here as these are absolute unchanging dir convention used by phoenix.
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
const documentsDIR = window.fs.getTauriVirtualPath(window._tauriBootVars.documentDir);
expect(brackets.app.getUserDocumentsDirectory().startsWith("/tauri/")).toBeTrue();
expect(brackets.app.getUserDocumentsDirectory()).toBe(documentsDIR);
@@ -817,7 +817,7 @@ define(function (require, exports, module) {
});
it("should have a user projects directory", function () {
// these tests are here as these are absolute unchanging dir convention used by phoenix.
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
const documentsDIR = window.fs.getTauriVirtualPath(window._tauriBootVars.documentDir);
const appName = window._tauriBootVars.appname;
const userProjectsDir = `${documentsDIR}${appName}/`;
@@ -829,7 +829,7 @@ define(function (require, exports, module) {
});
it("should have a temp directory", function () {
// these tests are here as these are absolute unchanging dir convention used by phoenix.
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
let tempDIR = window.fs.getTauriVirtualPath(window._tauriBootVars.tempDir);
if(!tempDIR.endsWith("/")){
tempDIR = `${tempDIR}/`;
@@ -844,7 +844,7 @@ define(function (require, exports, module) {
});
it("should have extensions directory", function () {
// these tests are here as these are absolute unchanging dir convention used by phoenix.
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
const appSupportDIR = window.fs.getTauriVirtualPath(window._tauriBootVars.appLocalDir);
const extensionsDir = `${appSupportDIR}assets/extensions/`;
expect(brackets.app.getExtensionsDirectory().startsWith("/tauri/")).toBeTrue();
@@ -855,7 +855,7 @@ define(function (require, exports, module) {
});
it("should get virtual serving directory from virtual serving URL in browser", async function () {
- if(window.__TAURI__){
+ if(Phoenix.isNativeApp){
return;
}
expect(brackets.VFS.getPathForVirtualServingURL(`${window.fsServerUrl}blinker`)).toBe("/blinker");
@@ -866,7 +866,7 @@ define(function (require, exports, module) {
});
it("should not get virtual serving directory from virtual serving URL in tauri", async function () {
- if(!window.__TAURI__){
+ if(!Phoenix.isNativeApp){
return;
}
expect(window.fsServerUrl).not.toBeDefined();
diff --git a/test/spec/Native-platform-test.js b/test/spec/Native-platform-test.js
new file mode 100644
index 0000000000..0e4ca767c3
--- /dev/null
+++ b/test/spec/Native-platform-test.js
@@ -0,0 +1,579 @@
+/*
+ * GNU AGPL-3.0 License
+ *
+ * Copyright (c) 2021 - present core.ai . All rights reserved.
+ * Original work Copyright (c) 2013 - 2021 Adobe Systems Incorporated. All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
+ *
+ */
+
+/*global describe, it, expect, beforeEach, afterEach, fs, path, jasmine, expectAsync*/
+
+define(function (require, exports, module) {
+ // Platform detection
+ const isElectron = !!window.__ELECTRON__;
+ const isTauri = !!window.__TAURI__;
+
+ if (!isElectron && !isTauri) {
+ return;
+ }
+
+ const SpecRunnerUtils = require("spec/SpecRunnerUtils");
+
+ // Platform abstraction helpers - same tests, different API calls
+ const platform = {
+ name: isElectron ? 'Electron' : 'Tauri',
+
+ // Path APIs
+ appLocalDataDir: () => isElectron
+ ? window.electronFSAPI.appLocalDataDir()
+ : window.__TAURI__.path.appLocalDataDir(),
+
+ documentDir: () => isElectron
+ ? window._tauriBootVars.documentDir // Same source for both
+ : window._tauriBootVars.documentDir,
+
+ // Asset URL conversion
+ convertToAssetURL: (platformPath) => isElectron
+ ? window.electronAPI.convertToAssetURL(platformPath)
+ : window.__TAURI__.tauri.convertFileSrc(platformPath),
+
+ // Credential APIs
+ storeCredential: (scopeName, secretVal) => isElectron
+ ? window.electronAPI.storeCredential(scopeName, secretVal)
+ : window.__TAURI__.invoke("store_credential", { scopeName, secretVal }),
+
+ getCredential: (scopeName) => isElectron
+ ? window.electronAPI.getCredential(scopeName)
+ : window.__TAURI__.invoke("get_credential", { scopeName }),
+
+ deleteCredential: (scopeName) => isElectron
+ ? window.electronAPI.deleteCredential(scopeName)
+ : window.__TAURI__.invoke("delete_credential", { scopeName }),
+
+ // Trust ring APIs
+ trustWindowAesKey: (keyIv) => isElectron
+ ? window.electronAPI.trustWindowAesKey(keyIv.key, keyIv.iv)
+ : window.__TAURI__.tauri.invoke("trust_window_aes_key", keyIv),
+
+ removeTrustWindowAesKey: (keyIv) => isElectron
+ ? window.electronAPI.removeTrustWindowAesKey(keyIv.key, keyIv.iv)
+ : window.__TAURI__.tauri.invoke("remove_trust_window_aes_key", keyIv),
+
+ // Window management - returns platform-specific window object
+ // For window spawning tests, we use a helper HTML file
+ getTestHtmlPath: () => isElectron
+ ? 'spec/native-platform-electron-test.html'
+ : 'spec/native-platform-tauri-test.html',
+
+ // Close window by label (uses platform-agnostic Phoenix.app API)
+ closeWindow: (windowObj) => Phoenix.app.closeWindowByLabel(windowObj.label)
+ };
+
+ describe(`unit: ${platform.name} Platform Tests`, function () {
+
+ beforeEach(async function () {
+
+ });
+
+ afterEach(async function () {
+
+ });
+
+ describe("asset url tests", function () {
+ it("Should be able to fetch files in {appLocalData}/assets folder", async function () {
+ const appLocalData = fs.getTauriVirtualPath(await platform.appLocalDataDir());
+ expect(await SpecRunnerUtils.pathExists(appLocalData, true)).toBeTrue();
+ expect(appLocalData.split("/")[1]).toEql("tauri"); // should be /tauri/applocaldata/path
+
+ // now write a test html file to the assets folder
+ const assetHTMLPath = `${appLocalData}/assets/a9322657236.html`;
+ const assetHtmlText = "Hello world random37834324";
+ await SpecRunnerUtils.ensureExistsDirAsync(path.dirname(assetHTMLPath));
+ await SpecRunnerUtils.createTextFileAsync(assetHTMLPath, assetHtmlText);
+
+ const appLocalDataPlatformPath = fs.getTauriPlatformPath(assetHTMLPath);
+ const appLocalDataURL = platform.convertToAssetURL(appLocalDataPlatformPath);
+
+ const fetchedData = await ((await fetch(appLocalDataURL)).text());
+ expect(fetchedData).toEqual(assetHtmlText);
+
+ // delete test file
+ await SpecRunnerUtils.deletePathAsync(assetHTMLPath);
+ });
+
+ async function testAssetNotAccessibleFolder(platformPath) {
+ const assets = fs.getTauriVirtualPath(platformPath);
+ expect(assets.split("/")[1]).toEql("tauri"); // should be /tauri/applocaldata/path
+
+ // now write a test html file to the assets folder
+ const assetHTMLPath = `${assets}/a9322657236.html`;
+ const assetHtmlText = "Hello world random37834324";
+ await SpecRunnerUtils.createTextFileAsync(assetHTMLPath, assetHtmlText);
+
+ const appLocalDataPlatformPath = fs.getTauriPlatformPath(assetHTMLPath);
+ const appLocalDataURL = platform.convertToAssetURL(appLocalDataPlatformPath);
+
+ // Tauri throws an error, Electron returns 403 response
+ let accessDenied = false;
+ try {
+ const response = await fetch(appLocalDataURL);
+ // Electron returns 403 for unauthorized access
+ if (!response.ok) {
+ accessDenied = true;
+ }
+ } catch (e) {
+ // Tauri throws an error
+ accessDenied = true;
+ }
+ expect(accessDenied).withContext("Asset URL should not be accessible outside assets folder").toBeTrue();
+
+ // delete test file
+ await SpecRunnerUtils.deletePathAsync(assetHTMLPath);
+ }
+
+ it("Should not be able to fetch files in documents folder", async function () {
+ // unfortunately for tests, this is set to appdata/testDocuments.
+ // we cant set this to await window.__TAURI__.path.documentDir() as in github actions,
+ // the user documents directory is not defined in rust and throws.
+ await testAssetNotAccessibleFolder(platform.documentDir());
+ });
+
+ it("Should not be able to fetch files in appLocalData folder", async function () {
+ await testAssetNotAccessibleFolder(await platform.appLocalDataDir());
+ });
+
+ // Electron-specific security test: verify asset:// URLs don't have API access
+ if (isElectron) {
+ it("Should NOT have electronAPI access from asset:// protocol", async function () {
+ // This test verifies that content loaded from asset:// URLs is sandboxed
+ // and does not have access to Electron APIs (matching Tauri's security posture)
+ const appLocalData = fs.getTauriVirtualPath(await platform.appLocalDataDir());
+ const securityTestPath = `${appLocalData}/assets/security-test-${Date.now()}.html`;
+ const SECURITY_TEST_KEY = 'ELECTRON_ASSET_SECURITY_TEST_' + Date.now();
+
+ // Create the security test HTML in assets folder
+ // This script will try to use electronAPI if available and report back
+ const securityTestHtml = `
+Security Test`;
+
+ await SpecRunnerUtils.ensureExistsDirAsync(path.dirname(securityTestPath));
+ await SpecRunnerUtils.createTextFileAsync(securityTestPath, securityTestHtml);
+
+ // Get the asset:// URL for the test file
+ const platformPath = fs.getTauriPlatformPath(securityTestPath);
+ const assetURL = platform.convertToAssetURL(platformPath);
+
+ // Clear any previous test result
+ await window.electronAPI.putItem(SECURITY_TEST_KEY, null);
+
+ // Try to open a window with the asset:// URL
+ let windowLabel = null;
+ try {
+ windowLabel = await window.electronAPI.createPhoenixWindow(assetURL, {
+ windowTitle: 'Security Test',
+ width: 400,
+ height: 300,
+ isExtension: true
+ });
+ } catch (e) {
+ // If window creation fails for asset:// URLs, that's acceptable security behavior
+ console.log("Window creation blocked for asset:// URL (expected):", e.message);
+ }
+
+ if (windowLabel) {
+ // Window was created - wait for it to load and potentially try to use APIs
+ await new Promise(resolve => setTimeout(resolve, 2000));
+
+ // Check if the sandboxed window was able to use electronAPI
+ const items = await window.electronAPI.getAllItems();
+ const testResult = items[SECURITY_TEST_KEY];
+
+ if (testResult) {
+ const parsed = JSON.parse(testResult);
+ // SECURITY CHECK: If we got a result, verify no API access was possible
+ // If any of these are true, it's a security vulnerability
+ expect(parsed.hasElectronAPI).withContext(
+ "SECURITY VIOLATION: asset:// window has electronAPI access"
+ ).toBeFalse();
+ expect(parsed.hasElectronFSAPI).withContext(
+ "SECURITY VIOLATION: asset:// window has electronFSAPI access"
+ ).toBeFalse();
+ expect(parsed.hasElectronAppAPI).withContext(
+ "SECURITY VIOLATION: asset:// window has electronAppAPI access"
+ ).toBeFalse();
+ }
+ // If no result was stored, the window couldn't access APIs - test passes
+
+ // Close the security test window by its label
+ try {
+ await Phoenix.app.closeWindowByLabel(windowLabel);
+ } catch (e) {
+ console.warn("Could not close security test window:", e);
+ }
+ }
+
+ // Cleanup
+ await SpecRunnerUtils.deletePathAsync(securityTestPath);
+ });
+ }
+
+ function createWebView() {
+ return new Promise((resolve, reject) => {
+ let currentURL = new URL(location.href);
+ let pathParts = currentURL.pathname.split('/');
+ pathParts[pathParts.length - 1] = platform.getTestHtmlPath();
+ currentURL.pathname = pathParts.join('/');
+
+ let newURL = currentURL.href;
+
+ // Use unified event API for both platforms
+ let nativeWindow = null;
+ let eventReceived = false;
+
+ const tryResolve = () => {
+ if (nativeWindow && eventReceived) {
+ resolve(nativeWindow);
+ }
+ };
+
+ const unlisten = Phoenix.app.onWindowEvent('PLATFORM_API_WORKING', () => {
+ unlisten();
+ eventReceived = true;
+ tryResolve();
+ });
+
+ Phoenix.app.openURLInPhoenixWindow(newURL)
+ .then(win => {
+ expect(win.label.startsWith("extn-")).toBeTrue();
+ expect(win.isNativeWindow).toBeTrue();
+ nativeWindow = win;
+ tryResolve();
+ }).catch(err => {
+ unlisten();
+ reject(err);
+ });
+ });
+ }
+
+ it("Should be able to spawn windows", async function () {
+ const nativeWindow = await createWebView();
+ await platform.closeWindow(nativeWindow);
+ });
+
+ it("Should be able to get process ID", async function () {
+ const processID = await Phoenix.app.getProcessID();
+ expect(processID).toEqual(jasmine.any(Number));
+ });
+
+ const maxWindows = 25;
+ it(`Should be able to spawn ${maxWindows} windows`, async function () {
+ const nativeWindows = [];
+ for (let i = 0; i < maxWindows; i++) {
+ nativeWindows.push(await createWebView());
+ }
+ for (let i = 0; i < maxWindows; i++) {
+ await platform.closeWindow(nativeWindows[i]);
+ }
+ // Wait for windows to close
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ }, 120000);
+ });
+
+ describe("Inter-window Event API Tests", function () {
+ // Note: emitToAllWindows excludes the sender, so we test cross-window communication
+ // using spawned windows that emit PLATFORM_API_WORKING event
+
+ it("Should receive events from spawned windows using unified API", async function () {
+ let eventReceived = false;
+ let receivedPayload = null;
+ const unlisten = Phoenix.app.onWindowEvent('PLATFORM_API_WORKING', (payload) => {
+ eventReceived = true;
+ receivedPayload = payload;
+ });
+
+ // Small delay for listener registration (Tauri's listen is async)
+ await new Promise(resolve => setTimeout(resolve, 100));
+
+ let currentURL = new URL(location.href);
+ let pathParts = currentURL.pathname.split('/');
+ pathParts[pathParts.length - 1] = platform.getTestHtmlPath();
+ currentURL.pathname = pathParts.join('/');
+
+ const win = await Phoenix.app.openURLInPhoenixWindow(currentURL.href);
+ expect(win.label.startsWith("extn-")).toBeTrue();
+
+ // Wait for the spawned window to emit the event
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ expect(eventReceived).toBeTrue();
+ expect(receivedPayload).toBeDefined();
+
+ unlisten();
+ await platform.closeWindow(win);
+ });
+
+ it("Should unlisten properly and not receive events after unlisten", async function () {
+ let callCount = 0;
+ const unlisten = Phoenix.app.onWindowEvent('PLATFORM_API_WORKING', () => {
+ callCount++;
+ });
+
+ // Small delay for listener registration
+ await new Promise(resolve => setTimeout(resolve, 100));
+
+ // Spawn first window - should receive event
+ let currentURL = new URL(location.href);
+ let pathParts = currentURL.pathname.split('/');
+ pathParts[pathParts.length - 1] = platform.getTestHtmlPath();
+ currentURL.pathname = pathParts.join('/');
+
+ const win1 = await Phoenix.app.openURLInPhoenixWindow(currentURL.href);
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ expect(callCount).toBeGreaterThanOrEqual(1);
+ const countAfterFirst = callCount;
+
+ // Unlisten
+ unlisten();
+ await new Promise(resolve => setTimeout(resolve, 100));
+
+ // Spawn second window - should NOT receive event
+ const win2 = await Phoenix.app.openURLInPhoenixWindow(currentURL.href);
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ expect(callCount).toEqual(countAfterFirst); // Count should not increase
+
+ await platform.closeWindow(win1);
+ await platform.closeWindow(win2);
+ });
+
+ it("Should not throw when emitting events", async function () {
+ // Basic sanity test that emit APIs don't throw
+ await expectAsync(
+ Phoenix.app.emitToAllWindows('TEST_EVENT', { test: true })
+ ).toBeResolved();
+
+ await expectAsync(
+ Phoenix.app.emitToWindow('nonexistent-window', 'TEST_EVENT', { test: true })
+ ).toBeResolved();
+ });
+
+ it("Should return unlisten function from onWindowEvent", function () {
+ const unlisten = Phoenix.app.onWindowEvent('TEST_EVENT', () => {});
+ expect(typeof unlisten).toEqual('function');
+ unlisten(); // Should not throw
+ });
+ });
+
+ describe("Credentials OTP API Tests", function () {
+ const scopeName = "testScope";
+ const trustRing = window.specRunnerTestKernalModeTrust;
+ const TEST_TRUST_KEY_NAME = "testTrustKey";
+
+ function decryptCreds(creds) {
+ return trustRing.AESDecryptString(creds, trustRing.aesKeys.key, trustRing.aesKeys.iv);
+ }
+
+ beforeEach(async function () {
+ // Cleanup before running tests
+ await platform.deleteCredential(scopeName).catch(() => {});
+ });
+
+ afterEach(async function () {
+ // Cleanup after tests
+ await platform.deleteCredential(scopeName).catch(() => {});
+ });
+
+ if(Phoenix.isTestWindowGitHubActions && Phoenix.platform === "linux"){
+ // Credentials test doesn't work in GitHub actions in linux desktop as the runner cant reach key ring.
+ it("Should not run in github actions in linux desktop", async function () {
+ expect(1).toEqual(1);
+ });
+ return;
+ }
+
+ describe("Credential Storage & OTP Generation", function () {
+ it("Should store credentials successfully", async function () {
+ const randomUUID = crypto.randomUUID();
+ await expectAsync(
+ platform.storeCredential(scopeName, randomUUID)
+ ).toBeResolved();
+ });
+
+ it("Should get credentials as encrypted string", async function () {
+ const randomUUID = crypto.randomUUID();
+ await platform.storeCredential(scopeName, randomUUID);
+
+ const response = await platform.getCredential(scopeName);
+ expect(response).toBeDefined();
+ expect(response).not.toEqual(randomUUID);
+ });
+
+ it("Should retrieve and decrypt set credentials with kernal mode keys", async function () {
+ const randomUUID = crypto.randomUUID();
+ await platform.storeCredential(scopeName, randomUUID);
+
+ const creds = await platform.getCredential(scopeName);
+ expect(creds).toBeDefined();
+ const decryptedString = await decryptCreds(creds);
+ expect(decryptedString).toEqual(randomUUID);
+ });
+
+ it("Should return null if credentials do not exist", async function () {
+ const response = await platform.getCredential(scopeName);
+ expect(response).toBeNull();
+ });
+
+ it("Should delete stored credentials", async function () {
+ const randomUUID = crypto.randomUUID();
+ await platform.storeCredential(scopeName, randomUUID);
+
+ // Ensure credential exists
+ let creds = await platform.getCredential(scopeName);
+ expect(creds).toBeDefined();
+
+ // Delete credential
+ await expectAsync(
+ platform.deleteCredential(scopeName)
+ ).toBeResolved();
+
+ // Ensure credential is deleted
+ creds = await platform.getCredential(scopeName);
+ expect(creds).toBeNull();
+ });
+
+ it("Should handle deletion of non-existent credentials gracefully", async function () {
+ let error;
+ try {
+ await platform.deleteCredential(scopeName);
+ } catch (err) {
+ error = err;
+ }
+
+ // The test should fail if no error was thrown
+ expect(error).toBeDefined();
+
+ // Check for OS-specific error messages
+ const expectedErrors = [
+ "No matching entry found in secure storage", // Common error on Linux/macOS
+ "The specified item could not be found in the keychain", // macOS Keychain
+ "Element not found" // Windows Credential Manager
+ ];
+
+ const isExpectedError = expectedErrors.some(msg => error.toString().includes(msg));
+ expect(isExpectedError).toBeTrue();
+ });
+
+ it("Should overwrite existing credentials when storing with the same scope", async function () {
+ const oldUUID = crypto.randomUUID();
+ await platform.storeCredential(scopeName, oldUUID);
+
+ let creds = await platform.getCredential(scopeName);
+ expect(creds).toBeDefined();
+ let response = await decryptCreds(creds);
+ expect(response).toEqual(oldUUID);
+
+ // Store new credentials with the same scope
+ const newUUID = crypto.randomUUID();
+ await platform.storeCredential(scopeName, newUUID);
+
+ creds = await platform.getCredential(scopeName);
+ expect(creds).toBeDefined();
+ response = await decryptCreds(creds);
+ expect(response).toEqual(newUUID);
+ });
+
+ // trustRing.getCredential and set tests
+ async function setSomeKey() {
+ const randomCred = crypto.randomUUID();
+ await trustRing.setCredential(TEST_TRUST_KEY_NAME, randomCred);
+ const savedCred = await trustRing.getCredential(TEST_TRUST_KEY_NAME);
+ expect(savedCred).toEqual(randomCred);
+ return savedCred;
+ }
+
+ it("Should get and set API key in kernal mode trust ring", async function () {
+ await setSomeKey();
+ });
+
+ it("Should get and set empty string API key in kernal mode trust ring", async function () {
+ const randomCred = "";
+ await trustRing.setCredential(TEST_TRUST_KEY_NAME, randomCred);
+ const savedCred = await trustRing.getCredential(TEST_TRUST_KEY_NAME);
+ expect(savedCred).toEqual(randomCred);
+ });
+
+ it("Should remove API key in kernal mode trust ring work as expected", async function () {
+ await setSomeKey();
+ await trustRing.removeCredential(TEST_TRUST_KEY_NAME);
+ const cred = await trustRing.getCredential(TEST_TRUST_KEY_NAME);
+ expect(cred).toBeNull();
+ });
+
+ // trust key management
+ it("Should not be able to set trust key if one is already set", async function () {
+ const kv = trustRing.generateRandomKeyAndIV();
+ let error;
+ try {
+ await platform.trustWindowAesKey(kv);
+ } catch (err) {
+ error = err;
+ }
+ expect(error.toString()).toContain("Trust has already been established for this window.");
+ });
+
+ it("Should be able to remove trust key with key and iv", async function () {
+ await platform.removeTrustWindowAesKey(trustRing.aesKeys);
+ let error;
+ try {
+ await platform.removeTrustWindowAesKey(trustRing.aesKeys);
+ } catch (err) {
+ error = err;
+ }
+ expect(error.toString()).toContain("No trust association found for this window.");
+ // reinstate trust
+ await platform.trustWindowAesKey(trustRing.aesKeys);
+ });
+
+ it("Should getCredential not work without trust", async function () {
+ await setSomeKey();
+ await platform.removeTrustWindowAesKey(trustRing.aesKeys);
+ let error;
+ try {
+ await trustRing.getCredential(TEST_TRUST_KEY_NAME);
+ } catch (err) {
+ error = err;
+ }
+ expect(error.toString()).toContain("Trust needs to be first established");
+ // reinstate trust
+ await platform.trustWindowAesKey(trustRing.aesKeys);
+ });
+ });
+ });
+ });
+});
diff --git a/test/spec/Storage-integ-test.js b/test/spec/Storage-integ-test.js
index 925a65cc25..2d18cbb673 100644
--- a/test/spec/Storage-integ-test.js
+++ b/test/spec/Storage-integ-test.js
@@ -122,7 +122,7 @@ define(function (require, exports, module) {
expect(val).toEql(expectedValue);
});
- it("Should be able to create lmdb dumps in tauri", async function () {
+ it("Should be able to create lmdb dumps in native app", async function () {
if(!Phoenix.isNativeApp){
return;
}
@@ -137,7 +137,14 @@ define(function (require, exports, module) {
});
const dumpFileLocation = await window.storageNodeConnector.execPeer("dumpDBToFile");
- const dumpFileText = await window.__TAURI__.fs.readTextFile(dumpFileLocation);
+ let dumpFileText;
+ if (window.__TAURI__) {
+ dumpFileText = await window.__TAURI__.fs.readTextFile(dumpFileLocation);
+ } else if (window.__ELECTRON__) {
+ const data = await window.electronFSAPI.fsReadFile(dumpFileLocation);
+ const decoder = new TextDecoder("utf-8");
+ dumpFileText = decoder.decode(data);
+ }
const dumpObj = JSON.parse(dumpFileText);
expect(dumpObj[key]).toEql(expectedValue);
});
diff --git a/test/spec/Tauri-platform-test.js b/test/spec/Tauri-platform-test.js
deleted file mode 100644
index f7cd733506..0000000000
--- a/test/spec/Tauri-platform-test.js
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * GNU AGPL-3.0 License
- *
- * Copyright (c) 2021 - present core.ai . All rights reserved.
- * Original work Copyright (c) 2013 - 2021 Adobe Systems Incorporated. All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
- *
- */
-
-/*global describe, it, expect, beforeEach, afterEach, fs, path, jasmine, expectAsync*/
-
-define(function (require, exports, module) {
- if(!window.__TAURI__) {
- return;
- }
-
- const SpecRunnerUtils = require("spec/SpecRunnerUtils");
-
- describe("unit: Tauri Platform Tests", function () {
-
- beforeEach(async function () {
-
- });
-
- afterEach(async function () {
-
- });
-
- describe("asset url tests", function () {
- it("Should be able to fetch files in {appLocalData}/assets folder", async function () {
- const appLocalData = fs.getTauriVirtualPath(await window.__TAURI__.path.appLocalDataDir());
- expect(await SpecRunnerUtils.pathExists(appLocalData, true)).toBeTrue();
- expect(appLocalData.split("/")[1]).toEql("tauri"); // should be /tauri/applocaldata/path
-
- // now write a test html file to the assets folder
- const assetHTMLPath = `${appLocalData}/assets/a9322657236.html`;
- const assetHtmlText = "Hello world random37834324";
- await SpecRunnerUtils.ensureExistsDirAsync(path.dirname(assetHTMLPath));
- await SpecRunnerUtils.createTextFileAsync(assetHTMLPath, assetHtmlText);
-
- const appLocalDataPlatformPath = fs.getTauriPlatformPath(assetHTMLPath);
- const appLocalDataURL = window.__TAURI__.tauri.convertFileSrc(appLocalDataPlatformPath);
-
- const fetchedData = await ((await fetch(appLocalDataURL)).text());
- expect(fetchedData).toEqual(assetHtmlText);
-
- // delete test file
- await SpecRunnerUtils.deletePathAsync(assetHTMLPath);
- });
-
- async function testAssetNotAccessibleFolder(platformPath) {
- const assets = fs.getTauriVirtualPath(platformPath);
- expect(assets.split("/")[1]).toEql("tauri"); // should be /tauri/applocaldata/path
-
- // now write a test html file to the assets folder
- const assetHTMLPath = `${assets}/a9322657236.html`;
- const assetHtmlText = "Hello world random37834324";
- await SpecRunnerUtils.createTextFileAsync(assetHTMLPath, assetHtmlText);
-
- const appLocalDataPlatformPath = fs.getTauriPlatformPath(assetHTMLPath);
- const appLocalDataURL = window.__TAURI__.tauri.convertFileSrc(appLocalDataPlatformPath);
-
- let err;
- try{
- await fetch(appLocalDataURL);
- } catch (e) {
- err = e;
- }
- expect(err).toBeDefined();
-
- // delete test file
- await SpecRunnerUtils.deletePathAsync(assetHTMLPath);
- }
-
- it("Should not be able to fetch files in documents folder", async function () {
- // unfortunately for tests, this is set to appdata/testDocuments.
- // we cant set this to await window.__TAURI__.path.documentDir() as in github actions,
- // the user documents directory is not defined in rust and throws.
- await testAssetNotAccessibleFolder(window._tauriBootVars.documentDir);
- });
-
- it("Should not be able to fetch files in appLocalData folder", async function () {
- await testAssetNotAccessibleFolder(await window.__TAURI__.path.appLocalDataDir());
- });
-
- function createWebView() {
- return new Promise((resolve, reject)=>{
- let currentURL = new URL(location.href);
- let pathParts = currentURL.pathname.split('/');
- pathParts[pathParts.length - 1] = 'spec/Tauri-platform-test.html';
- currentURL.pathname = pathParts.join('/');
-
- let newURL = currentURL.href;
- Phoenix.app.openURLInPhoenixWindow(newURL)
- .then(tauriWindow =>{
- expect(tauriWindow.label.startsWith("extn-")).toBeTrue();
- tauriWindow.listen('TAURI_API_WORKING', function () {
- resolve(tauriWindow);
- });
- }).catch(reject);
- });
-
- }
-
- it("Should be able to spawn tauri windows", async function () {
- const tauriWindow = await createWebView();
- await tauriWindow.close();
- });
-
- it("Should be able to get process ID", async function () {
- const processID = await Phoenix.app.getProcessID();
- expect(processID).toEqual(jasmine.any(Number));
- });
-
- const maxWindows = 25;
- it(`Should be able to spawn ${maxWindows} tauri windows`, async function () {
- const tauriWindows = [];
- for(let i=0; i {});
- });
-
- afterEach(async function () {
- // Cleanup after tests
- await window.__TAURI__.invoke("delete_credential", { scopeName }).catch(() => {});
- });
-
- if(Phoenix.isTestWindowGitHubActions && Phoenix.platform === "linux"){
- // Credentials test doesn't work in GitHub actions in linux desktop as the runner cant reach key ring.
- it("Should not run in github actions in linux desktop", async function () {
- expect(1).toEqual(1);
- });
- return;
- }
-
- describe("Credential Storage & OTP Generation", function () {
- it("Should store credentials successfully", async function () {
- const randomUUID = crypto.randomUUID();
- await expectAsync(
- window.__TAURI__.invoke("store_credential", { scopeName, secretVal: randomUUID })
- ).toBeResolved();
- });
-
- it("Should get credentials as encrypted string", async function () {
- const randomUUID = crypto.randomUUID();
- await window.__TAURI__.invoke("store_credential", { scopeName, secretVal: randomUUID });
-
- const response = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(response).toBeDefined();
- expect(response).not.toEqual(randomUUID);
- });
-
- it("Should retrieve and decrypt set credentials with kernal mode keys", async function () {
- const randomUUID = crypto.randomUUID();
- await window.__TAURI__.invoke("store_credential", { scopeName, secretVal: randomUUID });
-
- const creds = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(creds).toBeDefined();
- const decryptedString = await decryptCreds(creds);
- expect(decryptedString).toEqual(randomUUID);
- });
-
- it("Should return an error if credentials do not exist", async function () {
- const response = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(response).toBeNull();
- });
-
- it("Should delete stored credentials", async function () {
- const randomUUID = crypto.randomUUID();
- await window.__TAURI__.invoke("store_credential", { scopeName, secretVal: randomUUID });
-
- // Ensure credential exists
- let creds = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(creds).toBeDefined();
-
- // Delete credential
- await expectAsync(
- window.__TAURI__.invoke("delete_credential", { scopeName })
- ).toBeResolved();
-
- // Ensure credential is deleted
- creds = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(creds).toBeNull();
- });
-
- it("Should handle deletion of non-existent credentials gracefully", async function () {
- let error;
- try {
- await window.__TAURI__.invoke("delete_credential", { scopeName });
- } catch (err) {
- error = err;
- }
-
- // The test should fail if no error was thrown
- expect(error).toBeDefined();
-
- // Check for OS-specific error messages
- const expectedErrors = [
- "No matching entry found in secure storage", // Common error on Linux/macOS
- "The specified item could not be found in the keychain", // macOS Keychain
- "Element not found" // Windows Credential Manager
- ];
-
- const isExpectedError = expectedErrors.some(msg => error.includes(msg));
- expect(isExpectedError).toBeTrue();
- });
-
- it("Should overwrite existing credentials when storing with the same scope", async function () {
- const oldUUID = crypto.randomUUID();
- await window.__TAURI__.invoke("store_credential", { scopeName, secretVal: oldUUID });
-
- let creds = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(creds).toBeDefined();
- let response = await decryptCreds(creds);
- expect(response).toEqual(oldUUID);
-
- // Store new credentials with the same scope
- const newUUID = crypto.randomUUID();
- await window.__TAURI__.invoke("store_credential", { scopeName, secretVal: newUUID });
-
- creds = await window.__TAURI__.invoke("get_credential", { scopeName });
- expect(creds).toBeDefined();
- response = await decryptCreds(creds);
- expect(response).toEqual(newUUID);
- });
-
- // trustRing.getCredential and set tests
- async function setSomeKey() {
- const randomCred = crypto.randomUUID();
- await trustRing.setCredential(TEST_TRUST_KEY_NAME, randomCred);
- const savedCred = await trustRing.getCredential(TEST_TRUST_KEY_NAME);
- expect(savedCred).toEqual(randomCred);
- return savedCred;
- }
-
- it("Should get and set API key in kernal mode trust ring", async function () {
- await setSomeKey();
- });
-
- it("Should get and set empty string API key in kernal mode trust ring", async function () {
- const randomCred = "";
- await trustRing.setCredential(TEST_TRUST_KEY_NAME, randomCred);
- const savedCred = await trustRing.getCredential(TEST_TRUST_KEY_NAME);
- expect(savedCred).toEqual(randomCred);
- });
-
- it("Should remove API key in kernal mode trust ring work as expected", async function () {
- await setSomeKey();
- await trustRing.removeCredential(TEST_TRUST_KEY_NAME);
- const cred = await trustRing.getCredential(TEST_TRUST_KEY_NAME);
- expect(cred).toBeNull();
- });
-
- // trust key management
- it("Should not be able to set trust key if one is already set", async function () {
- const kv = trustRing.generateRandomKeyAndIV();
- let error;
- try {
- await window.__TAURI__.tauri.invoke("trust_window_aes_key", kv);
- } catch (err) {
- error = err;
- }
- expect(error).toContain("Trust has already been established for this window.");
- });
-
- it("Should be able to remove trust key with key and iv", async function () {
- await window.__TAURI__.tauri.invoke("remove_trust_window_aes_key", trustRing.aesKeys);
- let error;
- try {
- await window.__TAURI__.tauri.invoke("remove_trust_window_aes_key", trustRing.aesKeys);
- } catch (err) {
- error = err;
- }
- expect(error).toContain("No trust association found for this window.");
- // reinstate trust
- await window.__TAURI__.tauri.invoke("trust_window_aes_key", trustRing.aesKeys);
- });
-
- it("Should getCredential not work without trust", async function () {
- await setSomeKey();
- await window.__TAURI__.tauri.invoke("remove_trust_window_aes_key", trustRing.aesKeys);
- let error;
- try {
- await trustRing.getCredential(TEST_TRUST_KEY_NAME);
- } catch (err) {
- error = err;
- }
- expect(error).toContain("Trust needs to be first established");
- // reinstate trust
- await window.__TAURI__.tauri.invoke("trust_window_aes_key", trustRing.aesKeys);
- });
- });
- });
- });
-});
diff --git a/test/spec/native-platform-electron-test.html b/test/spec/native-platform-electron-test.html
new file mode 100644
index 0000000000..1c24add2c1
--- /dev/null
+++ b/test/spec/native-platform-electron-test.html
@@ -0,0 +1,25 @@
+
+
+
+
+ Test electron apis accessible
+
+
+
+sending event with electron api...
+
+
diff --git a/test/spec/Tauri-platform-test.html b/test/spec/native-platform-tauri-test.html
similarity index 55%
rename from test/spec/Tauri-platform-test.html
rename to test/spec/native-platform-tauri-test.html
index 14732a90e3..55035470a5 100644
--- a/test/spec/Tauri-platform-test.html
+++ b/test/spec/native-platform-tauri-test.html
@@ -4,7 +4,8 @@
Test tauri apis accessible