@flighthq/shell 0.4.0-next.1898.266773a → 0.4.0-next.1908.751021c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/shell.d.ts +5 -1
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +68 -56
- package/dist/shell.js.map +1 -1
- package/package.json +2 -2
- package/src/shell.test.ts +67 -27
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { isShellUrlAllowed, moveItemToTrash, moveItemsToTrash, openShellExternalUrl, openShellPath, openShellPathResult, readShellShortcutLink, setShellUrlSchemeAllowlist, shellBeep, showItemInFolder, writeShellShortcutLink, } from './contract';
|
|
1
|
+
export { explainShellBackend, isShellUrlAllowed, moveItemToTrash, moveItemsToTrash, openShellExternalUrl, openShellPath, openShellPathResult, readShellShortcutLink, setShellUrlSchemeAllowlist, shellBeep, showItemInFolder, writeShellShortcutLink, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,SAAS,EACT,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,SAAS,EACT,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { isShellUrlAllowed, moveItemToTrash, moveItemsToTrash, openShellExternalUrl, openShellPath, openShellPathResult, readShellShortcutLink, setShellUrlSchemeAllowlist, shellBeep, showItemInFolder, writeShellShortcutLink, } from './contract';
|
|
1
|
+
export { explainShellBackend, isShellUrlAllowed, moveItemToTrash, moveItemsToTrash, openShellExternalUrl, openShellPath, openShellPathResult, readShellShortcutLink, setShellUrlSchemeAllowlist, shellBeep, showItemInFolder, writeShellShortcutLink, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,SAAS,EACT,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,SAAS,EACT,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
|
package/dist/shell.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import type { BackendExplanation } from '@flighthq/types/contract';
|
|
1
2
|
import type { ShellBackend, ShellOpenExternalOptions, ShellOpenPathOptions, ShellShortcutLink, ShellShortcutWriteOperation } from '@flighthq/types/contract';
|
|
2
|
-
export declare function
|
|
3
|
+
export declare function explainShellBackend(): BackendExplanation;
|
|
3
4
|
export declare function getShellBackend(): ShellBackend;
|
|
5
|
+
export declare function installShellHostBackend(backend: ShellBackend): void;
|
|
4
6
|
export declare function isShellUrlAllowed(url: string): boolean;
|
|
5
7
|
export declare function moveItemsToTrash(paths: readonly string[]): Promise<readonly boolean[]>;
|
|
6
8
|
export declare function moveItemToTrash(path: string): Promise<boolean>;
|
|
9
|
+
export declare function observeShellHostResult(operation: string, succeeded: boolean): void;
|
|
7
10
|
export declare function openShellExternalUrl(url: string, options?: Readonly<ShellOpenExternalOptions>): Promise<boolean>;
|
|
8
11
|
export declare function openShellPath(path: string, options?: Readonly<ShellOpenPathOptions>): Promise<boolean>;
|
|
9
12
|
export declare function openShellPathResult(path: string, options?: Readonly<ShellOpenPathOptions>): Promise<string>;
|
|
10
13
|
export declare function readShellShortcutLink(shortcutPath: string): Promise<ShellShortcutLink | null>;
|
|
14
|
+
export declare function resetShellBackendForTest(): void;
|
|
11
15
|
export declare function setShellBackend(backend: ShellBackend | null): void;
|
|
12
16
|
export declare function setShellUrlSchemeAllowlist(schemes: readonly string[] | null): void;
|
|
13
17
|
export declare function shellBeep(): void;
|
package/dist/shell.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,mBAAmB,IAAI,kBAAkB,CAaxD;AAGD,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAMnE;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQtD;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAEtF;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9D;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAKlF;AAQD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,wBAAwB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAGhH;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAEtG;AAKD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3G;AAID,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAE7F;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C;AAGD,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAElE;AAMD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAElF;AAGD,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE/D;AAMD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACjC,SAAS,CAAC,EAAE,2BAA2B,GACtC,OAAO,CAAC,OAAO,CAAC,CAElB"}
|
package/dist/shell.js
CHANGED
|
@@ -1,58 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// The web cannot move a local path to the OS trash; native-host only.
|
|
15
|
-
return false;
|
|
16
|
-
},
|
|
17
|
-
async openExternal(url) {
|
|
18
|
-
// window.open returns a Window handle on success, or null when blocked (popup blocker, no window).
|
|
19
|
-
// The activate option (macOS foreground raise) has no web equivalent and is silently ignored.
|
|
20
|
-
if (typeof window === 'undefined' || typeof window.open !== 'function')
|
|
21
|
-
return false;
|
|
22
|
-
try {
|
|
23
|
-
return window.open(url, '_blank', 'noopener') !== null;
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
async openPath() {
|
|
30
|
-
// The web sandbox cannot open arbitrary local paths; only a native host can.
|
|
31
|
-
return false;
|
|
32
|
-
},
|
|
33
|
-
async openPathResult() {
|
|
34
|
-
// The web cannot open arbitrary local paths; native-host only.
|
|
35
|
-
return 'unavailable on web';
|
|
36
|
-
},
|
|
37
|
-
async readShortcutLink() {
|
|
38
|
-
// Windows .lnk shortcut links are not accessible on the web.
|
|
39
|
-
return null;
|
|
40
|
-
},
|
|
41
|
-
async showItemInFolder() {
|
|
42
|
-
// The web has no OS file manager to reveal an item in; native-host only.
|
|
43
|
-
return false;
|
|
44
|
-
},
|
|
45
|
-
async writeShortcutLink() {
|
|
46
|
-
// Windows .lnk shortcut links cannot be written on the web.
|
|
47
|
-
return false;
|
|
48
|
-
},
|
|
49
|
-
};
|
|
1
|
+
export function explainShellBackend() {
|
|
2
|
+
if (_custom !== null) {
|
|
3
|
+
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
4
|
+
}
|
|
5
|
+
if (_host !== null) {
|
|
6
|
+
return {
|
|
7
|
+
conflict: _hostConflict,
|
|
8
|
+
layer: 'host',
|
|
9
|
+
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
10
|
+
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
|
|
50
14
|
}
|
|
51
|
-
// The active shell backend
|
|
15
|
+
// The active shell backend. Precedence: custom > host > sentinel.
|
|
52
16
|
export function getShellBackend() {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
17
|
+
return _custom ?? _host ?? _sentinel;
|
|
18
|
+
}
|
|
19
|
+
export function installShellHostBackend(backend) {
|
|
20
|
+
if (_host !== null) {
|
|
21
|
+
if (_host !== backend)
|
|
22
|
+
_hostConflict = true;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
_host = backend;
|
|
56
26
|
}
|
|
57
27
|
// True when url is allowed by the active URL-scheme allowlist. When no allowlist is set, all URLs
|
|
58
28
|
// are allowed. Used internally by openShellExternalUrl; also exported for callers that need the check.
|
|
@@ -76,6 +46,12 @@ export function moveItemsToTrash(paths) {
|
|
|
76
46
|
export function moveItemToTrash(path) {
|
|
77
47
|
return getShellBackend().moveToTrash(path);
|
|
78
48
|
}
|
|
49
|
+
export function observeShellHostResult(operation, succeeded) {
|
|
50
|
+
_hostObservation = {
|
|
51
|
+
operation,
|
|
52
|
+
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
79
55
|
// Opens a URL in the user's default browser / external handler. Returns false when blocked, popup-
|
|
80
56
|
// blocked, or the URL scheme is not in the active allowlist (see setShellUrlSchemeAllowlist).
|
|
81
57
|
// SECURITY: handing an attacker-controlled URL to the OS default handler is the classic openExternal
|
|
@@ -102,9 +78,15 @@ export function openShellPathResult(path, options) {
|
|
|
102
78
|
export function readShellShortcutLink(shortcutPath) {
|
|
103
79
|
return getShellBackend().readShortcutLink(shortcutPath);
|
|
104
80
|
}
|
|
105
|
-
|
|
81
|
+
export function resetShellBackendForTest() {
|
|
82
|
+
_custom = null;
|
|
83
|
+
_host = null;
|
|
84
|
+
_hostConflict = false;
|
|
85
|
+
_hostObservation = null;
|
|
86
|
+
}
|
|
87
|
+
// Installs a custom shell backend; pass null to clear the custom override.
|
|
106
88
|
export function setShellBackend(backend) {
|
|
107
|
-
|
|
89
|
+
_custom = backend;
|
|
108
90
|
}
|
|
109
91
|
// Sets the URL-scheme allowlist consulted by openShellExternalUrl. Pass null to allow all schemes
|
|
110
92
|
// (default behavior). When a non-null list is set, openShellExternalUrl returns false for any URL whose
|
|
@@ -128,6 +110,36 @@ export function showItemInFolder(path) {
|
|
|
128
110
|
export function writeShellShortcutLink(shortcutPath, link, operation) {
|
|
129
111
|
return getShellBackend().writeShortcutLink(shortcutPath, link, operation);
|
|
130
112
|
}
|
|
131
|
-
|
|
113
|
+
const _sentinel = {
|
|
114
|
+
beep() { },
|
|
115
|
+
async moveItemsToTrash() {
|
|
116
|
+
return [];
|
|
117
|
+
},
|
|
118
|
+
async moveToTrash() {
|
|
119
|
+
return false;
|
|
120
|
+
},
|
|
121
|
+
async openExternal() {
|
|
122
|
+
return false;
|
|
123
|
+
},
|
|
124
|
+
async openPath() {
|
|
125
|
+
return false;
|
|
126
|
+
},
|
|
127
|
+
async openPathResult() {
|
|
128
|
+
return 'unavailable on web';
|
|
129
|
+
},
|
|
130
|
+
async readShortcutLink() {
|
|
131
|
+
return null;
|
|
132
|
+
},
|
|
133
|
+
async showItemInFolder() {
|
|
134
|
+
return false;
|
|
135
|
+
},
|
|
136
|
+
async writeShortcutLink() {
|
|
137
|
+
return false;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
let _custom = null;
|
|
141
|
+
let _host = null;
|
|
142
|
+
let _hostConflict = false;
|
|
143
|
+
let _hostObservation = null;
|
|
132
144
|
let _urlSchemeAllowlist = null;
|
|
133
145
|
//# sourceMappingURL=shell.js.map
|
package/dist/shell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/shell",
|
|
3
|
-
"version": "0.4.0-next.
|
|
3
|
+
"version": "0.4.0-next.1908.751021c",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/types": "0.4.0-next.
|
|
41
|
+
"@flighthq/types": "0.4.0-next.1908.751021c"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"typescript": "^5.3.0"
|
package/src/shell.test.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { ShellBackend, ShellOpenExternalOptions, ShellOpenPathOptions } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
explainShellBackend,
|
|
5
5
|
getShellBackend,
|
|
6
|
+
installShellHostBackend,
|
|
6
7
|
isShellUrlAllowed,
|
|
7
8
|
moveItemsToTrash,
|
|
8
9
|
moveItemToTrash,
|
|
10
|
+
observeShellHostResult,
|
|
9
11
|
openShellExternalUrl,
|
|
10
12
|
openShellPath,
|
|
11
13
|
openShellPathResult,
|
|
12
14
|
readShellShortcutLink,
|
|
15
|
+
resetShellBackendForTest,
|
|
13
16
|
setShellBackend,
|
|
14
17
|
setShellUrlSchemeAllowlist,
|
|
15
18
|
shellBeep,
|
|
@@ -72,40 +75,33 @@ function fakeBackend(): ShellBackend & {
|
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
afterEach(() => {
|
|
75
|
-
|
|
78
|
+
resetShellBackendForTest();
|
|
76
79
|
setShellUrlSchemeAllowlist(null);
|
|
77
80
|
});
|
|
78
81
|
|
|
79
|
-
describe('
|
|
80
|
-
it('returns
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(() => backend.beep()).not.toThrow();
|
|
82
|
+
describe('explainShellBackend', () => {
|
|
83
|
+
it('returns host-not-enabled when no backend is installed', () => {
|
|
84
|
+
expect(explainShellBackend()).toEqual({
|
|
85
|
+
conflict: false,
|
|
86
|
+
layer: 'host-not-enabled',
|
|
87
|
+
operation: null,
|
|
88
|
+
viability: 'unobserved',
|
|
89
|
+
});
|
|
88
90
|
});
|
|
89
91
|
|
|
90
|
-
it('
|
|
91
|
-
|
|
92
|
-
expect(
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('openPathResult returns the unavailable-on-web sentinel string', async () => {
|
|
96
|
-
const backend = createWebShellBackend();
|
|
97
|
-
expect(await backend.openPathResult('/tmp/x')).toBe('unavailable on web');
|
|
92
|
+
it('returns custom layer when a custom backend is set', () => {
|
|
93
|
+
setShellBackend(fakeBackend());
|
|
94
|
+
expect(explainShellBackend().layer).toBe('custom');
|
|
98
95
|
});
|
|
99
96
|
|
|
100
|
-
it('
|
|
101
|
-
|
|
102
|
-
expect(
|
|
103
|
-
expect(typeof (await backend.openExternal('https://example.com', { activate: true }))).toBe('boolean');
|
|
97
|
+
it('returns host layer when a host backend is installed', () => {
|
|
98
|
+
installShellHostBackend(fakeBackend());
|
|
99
|
+
expect(explainShellBackend().layer).toBe('host');
|
|
104
100
|
});
|
|
105
101
|
});
|
|
106
102
|
|
|
107
103
|
describe('getShellBackend', () => {
|
|
108
|
-
it('falls back to
|
|
104
|
+
it('falls back to sentinel', () => {
|
|
109
105
|
expect(getShellBackend()).not.toBeNull();
|
|
110
106
|
});
|
|
111
107
|
|
|
@@ -116,6 +112,23 @@ describe('getShellBackend', () => {
|
|
|
116
112
|
});
|
|
117
113
|
});
|
|
118
114
|
|
|
115
|
+
describe('installShellHostBackend', () => {
|
|
116
|
+
it('first-host-wins and sets conflict on second different backend', () => {
|
|
117
|
+
const first = fakeBackend();
|
|
118
|
+
const second = fakeBackend();
|
|
119
|
+
installShellHostBackend(first);
|
|
120
|
+
installShellHostBackend(second);
|
|
121
|
+
expect(explainShellBackend().conflict).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('does not set conflict when the same backend is installed twice', () => {
|
|
125
|
+
const backend = fakeBackend();
|
|
126
|
+
installShellHostBackend(backend);
|
|
127
|
+
installShellHostBackend(backend);
|
|
128
|
+
expect(explainShellBackend().conflict).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
119
132
|
describe('isShellUrlAllowed', () => {
|
|
120
133
|
it('returns true when no allowlist is set', () => {
|
|
121
134
|
expect(isShellUrlAllowed('https://example.com')).toBe(true);
|
|
@@ -159,6 +172,24 @@ describe('moveItemToTrash', () => {
|
|
|
159
172
|
});
|
|
160
173
|
});
|
|
161
174
|
|
|
175
|
+
describe('observeShellHostResult', () => {
|
|
176
|
+
it('records a successful observation into the explanation', () => {
|
|
177
|
+
installShellHostBackend(fakeBackend());
|
|
178
|
+
observeShellHostResult('openExternal', true);
|
|
179
|
+
const explanation = explainShellBackend();
|
|
180
|
+
expect(explanation.operation).toBe('openExternal');
|
|
181
|
+
expect(explanation.viability).toBe('available');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('records a failed observation into the explanation', () => {
|
|
185
|
+
installShellHostBackend(fakeBackend());
|
|
186
|
+
observeShellHostResult('openExternal', false);
|
|
187
|
+
const explanation = explainShellBackend();
|
|
188
|
+
expect(explanation.operation).toBe('openExternal');
|
|
189
|
+
expect(explanation.viability).toBe('runtime-api-unavailable');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
162
193
|
describe('openShellExternalUrl', () => {
|
|
163
194
|
it('opens via the active backend', async () => {
|
|
164
195
|
const backend = fakeBackend();
|
|
@@ -246,7 +277,7 @@ describe('openShellPathResult', () => {
|
|
|
246
277
|
});
|
|
247
278
|
|
|
248
279
|
describe('readShellShortcutLink', () => {
|
|
249
|
-
it('returns null
|
|
280
|
+
it('returns null with sentinel (no backend set)', async () => {
|
|
250
281
|
const result = await readShellShortcutLink('/tmp/x.lnk');
|
|
251
282
|
expect(result).toBeNull();
|
|
252
283
|
});
|
|
@@ -259,8 +290,17 @@ describe('readShellShortcutLink', () => {
|
|
|
259
290
|
});
|
|
260
291
|
});
|
|
261
292
|
|
|
293
|
+
describe('resetShellBackendForTest', () => {
|
|
294
|
+
it('clears all backend slots', () => {
|
|
295
|
+
setShellBackend(fakeBackend());
|
|
296
|
+
installShellHostBackend(fakeBackend());
|
|
297
|
+
resetShellBackendForTest();
|
|
298
|
+
expect(explainShellBackend().layer).toBe('host-not-enabled');
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
262
302
|
describe('setShellBackend', () => {
|
|
263
|
-
it('clears back to
|
|
303
|
+
it('clears back to sentinel when passed null', () => {
|
|
264
304
|
setShellBackend(fakeBackend());
|
|
265
305
|
setShellBackend(null);
|
|
266
306
|
expect(getShellBackend()).not.toBeNull();
|
|
@@ -299,7 +339,7 @@ describe('showItemInFolder', () => {
|
|
|
299
339
|
});
|
|
300
340
|
|
|
301
341
|
describe('writeShellShortcutLink', () => {
|
|
302
|
-
it('returns false
|
|
342
|
+
it('returns false with sentinel (no backend set)', async () => {
|
|
303
343
|
const result = await writeShellShortcutLink('/tmp/x.lnk', { target: '/tmp/target' });
|
|
304
344
|
expect(result).toBe(false);
|
|
305
345
|
});
|