@omega.js/desktop 0.1.0 → 0.51.0
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/README.md +1 -1
- package/dist/assets/css/tokens/_index.scss +1 -1
- package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +4 -1
- package/dist/assets/themes/base/_includes/frontend/sections/footer.html +13 -6
- package/dist/assets/themes/base/_includes/frontend/sections/nav.html +10 -8
- package/dist/assets/themes/base/_includes/global/sections/account.html +3 -1
- package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +12 -8
- package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +10 -6
- package/dist/assets/themes/base/_includes/global/sections/page-header.html +8 -4
- package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +24 -24
- package/dist/assets/themes/base/_layouts/frontend/pages/about.html +10 -10
- package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +35 -35
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +4 -4
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +4 -4
- package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +3 -3
- package/dist/assets/themes/base/_layouts/frontend/pages/download.html +34 -33
- package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +11 -11
- package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/status.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +9 -7
- package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +5 -3
- package/dist/assets/themes/base/_sections/about/letter/section.html +1 -1
- package/dist/assets/themes/base/_sections/about/letter/section.json5 +4 -4
- package/dist/assets/themes/base/_sections/marketing/bento/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +15 -15
- package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +1 -1
- package/dist/assets/themes/base/_sections/marketing/hero/section.html +6 -6
- package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +10 -10
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +2 -2
- package/dist/assets/themes/base/_sections/marketing/stats/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +5 -5
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +2 -2
- package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +10 -10
- package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +10 -10
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +2 -2
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +4 -4
- package/dist/build.js +69 -28
- package/dist/cli-run.js +20 -13
- package/dist/cli.js +12 -7
- package/dist/commands/build.js +0 -2
- package/dist/commands/deploy.js +116 -44
- package/dist/commands/finalize-release.js +5 -4
- package/dist/commands/launch.js +12 -7
- package/dist/commands/lib/deploy-precheck.js +66 -34
- package/dist/commands/lib/ensure-target.js +71 -11
- package/dist/commands/package.js +0 -1
- package/dist/commands/publish.js +11 -4
- package/dist/commands/release.js +99 -252
- package/dist/commands/runner.js +42 -5
- package/dist/commands/sign-windows.js +46 -15
- package/dist/commands/test.js +4 -3
- package/dist/commands/validate-certs.js +291 -115
- package/dist/config/page-template.html +4 -0
- package/dist/defaults/.github/workflows/build.yml +144 -64
- package/dist/defaults/AGENTS.md +3 -2
- package/dist/defaults/_.gitignore +4 -2
- package/dist/defaults/config/certs/README.md +25 -45
- package/dist/defaults/config/omega.json5 +66 -32
- package/dist/defaults/hooks/deploy/pre.js +10 -0
- package/dist/defaults/src/assets/scss/main.scss +12 -2
- package/dist/defaults/src/integrations/tray/index.js +1 -1
- package/dist/gulp/main.js +13 -25
- package/dist/gulp/tasks/audit.js +18 -6
- package/dist/gulp/tasks/build-config.js +181 -60
- package/dist/gulp/tasks/bundle.js +108 -36
- package/dist/gulp/tasks/release.js +86 -4
- package/dist/gulp/tasks/sass.js +11 -0
- package/dist/hooks/lib/notarize-tools.js +137 -0
- package/dist/hooks/notarize-artifacts.js +57 -0
- package/dist/hooks/notarize.js +59 -8
- package/dist/lib/auth-persistence.js +25 -0
- package/dist/lib/client-bridge.js +11 -8
- package/dist/lib/deep-link.js +15 -8
- package/dist/lib/protocol.js +7 -1
- package/dist/lib/restart-manager/install.js +6 -3
- package/dist/lib/sign-helpers/auto-unlock.js +65 -27
- package/dist/lib/sign-helpers/console-lock.js +34 -0
- package/dist/lib/sign-helpers/exec-with-limit.js +68 -0
- package/dist/lib/sign-helpers/resolve-icons.js +6 -4
- package/dist/lib/tray.js +7 -6
- package/dist/main.js +16 -0
- package/dist/preload.js +8 -0
- package/dist/renderer.js +16 -4
- package/dist/runner/job-started.js +104 -0
- package/dist/test/fixtures/consumer-app/config/omega.json5 +7 -0
- package/dist/test/fixtures/consumer-app/package.json +1 -1
- package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +7 -1
- package/dist/test/harness/main-entry.js +2 -1
- package/dist/test/harness/renderer-preload.js +12 -3
- package/dist/test/runners/boot.js +101 -11
- package/dist/test/runners/electron.js +1 -1
- package/dist/test/suites/boot/consumer-app-boots.test.js +54 -0
- package/dist/test/suites/build/audit.test.js +45 -6
- package/dist/test/suites/build/auth-persistence-resolve.test.js +119 -0
- package/dist/test/suites/build/auto-unlock.test.js +105 -0
- package/dist/test/suites/build/boot-runner-timeout.test.js +253 -0
- package/dist/test/suites/build/brand-scss.test.js +106 -0
- package/dist/test/suites/build/build-config.test.js +146 -36
- package/dist/test/suites/build/build-json-bake.test.js +245 -0
- package/dist/test/suites/build/build-verbs.test.js +48 -15
- package/dist/test/suites/build/build-workflow-jobs.test.js +190 -0
- package/dist/test/suites/build/cli.test.js +32 -10
- package/dist/test/suites/build/config-schema.test.js +4 -4
- package/dist/test/suites/build/console-lock.test.js +51 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +71 -2
- package/dist/test/suites/build/deploy-direct.test.js +241 -0
- package/dist/test/suites/build/deploy-dispatch.test.js +287 -0
- package/dist/test/suites/build/deploy-hook.test.js +169 -0
- package/dist/test/suites/build/ensure-target.test.js +14 -2
- package/dist/test/suites/build/env-delivery.test.js +19 -10
- package/dist/test/suites/build/env-watch.test.js +18 -7
- package/dist/test/suites/build/esm-only-dependency.test.js +127 -0
- package/dist/test/suites/build/exec-with-limit.test.js +53 -0
- package/dist/test/suites/build/finalize-release.test.js +2 -2
- package/dist/test/suites/build/get-config.test.js +120 -8
- package/dist/test/suites/build/github-utils.test.js +12 -6
- package/dist/test/suites/build/license-stamp.test.js +6 -4
- package/dist/test/suites/build/manager.test.js +88 -68
- package/dist/test/suites/build/manifest-deps.test.js +116 -0
- package/dist/test/suites/build/merge-line-files.test.js +27 -4
- package/dist/test/suites/build/notarize-artifacts.test.js +135 -0
- package/dist/test/suites/build/notarize-tools.test.js +38 -0
- package/dist/test/suites/build/notarize.test.js +207 -0
- package/dist/test/suites/build/release-pipeline.test.js +38 -60
- package/dist/test/suites/build/release-skipped-upload.test.js +107 -0
- package/dist/test/suites/build/resolve-icons.test.js +35 -35
- package/dist/test/suites/build/runner-job-guard.test.js +182 -0
- package/dist/test/suites/build/runner.test.js +25 -2
- package/dist/test/suites/build/sentry.test.js +8 -3
- package/dist/test/suites/build/setup-scripts.test.js +3 -0
- package/dist/test/suites/build/sign-windows.test.js +92 -8
- package/dist/test/suites/build/test-stealth.test.js +17 -11
- package/dist/test/suites/build/url-helpers.test.js +37 -17
- package/dist/test/suites/build/validate-certs.test.js +428 -55
- package/dist/test/suites/build/validate-config.test.js +3 -3
- package/dist/test/suites/main/auth-flow.test.js +12 -0
- package/dist/test/suites/main/auth-persistence.test.js +14 -17
- package/dist/test/suites/main/auto-updater.test.js +2 -2
- package/dist/test/suites/main/boot-sequence.test.js +1 -1
- package/dist/test/suites/main/client-bridge.integration.test.js +5 -82
- package/dist/test/suites/main/client-bridge.test.js +19 -6
- package/dist/test/suites/main/deep-link.test.js +54 -0
- package/dist/test/suites/main/startup-paths-and-ua.test.js +1 -1
- package/dist/test/suites/main/url-helpers.test.js +81 -72
- package/dist/test/suites/renderer/cross-context-helpers.test.js +19 -16
- package/dist/utils/build-pipeline.js +10 -10
- package/dist/utils/github.js +12 -51
- package/dist/utils/load-env.js +66 -0
- package/dist/utils/mode-helpers.js +43 -111
- package/dist/utils/platform.js +37 -0
- package/dist/utils/runner-env.js +2 -1
- package/dist/utils/runner-job-guard.js +149 -0
- package/dist/utils/ship-keys.js +52 -0
- package/dist/utils/test-stealth.js +5 -3
- package/dist/utils/url-helpers.js +33 -17
- package/dist/vendor/config/bundle-id.js +53 -0
- package/dist/vendor/config/client-config.js +141 -0
- package/dist/vendor/config/company.js +334 -15
- package/dist/vendor/config/dev-facts.js +48 -0
- package/dist/vendor/config/env-delivery.js +231 -9
- package/dist/vendor/config/env-retired.js +137 -0
- package/dist/vendor/config/env-rules.js +22 -3
- package/dist/vendor/config/env-schema.js +234 -117
- package/dist/vendor/config/env.js +55 -26
- package/dist/vendor/config/environment.js +189 -0
- package/dist/vendor/config/hooks.js +13 -11
- package/dist/vendor/config/index.js +121 -44
- package/dist/vendor/config/load.js +366 -115
- package/dist/vendor/config/merge.js +2 -2
- package/dist/vendor/config/order.js +3 -3
- package/dist/vendor/config/platforms.js +276 -0
- package/dist/vendor/config/repo.js +226 -104
- package/dist/vendor/config/retired-keys.js +232 -27
- package/dist/vendor/config/schema.js +525 -99
- package/dist/vendor/config/site-global.js +63 -53
- package/dist/vendor/config/targets.js +187 -0
- package/dist/vendor/config/validate.js +119 -59
- package/dist/vendor/devkit/argv.js +118 -0
- package/dist/vendor/devkit/attach-log-file.js +21 -13
- package/dist/vendor/devkit/brand-tokens.js +278 -0
- package/dist/vendor/devkit/brand-version.js +264 -0
- package/dist/vendor/devkit/build-json.js +91 -0
- package/dist/vendor/devkit/bundle.js +48 -0
- package/dist/vendor/devkit/certificate-expiry.js +108 -0
- package/dist/vendor/devkit/certs.js +16 -194
- package/dist/vendor/devkit/ci-workflows.js +124 -8
- package/dist/vendor/devkit/cli-router.js +3 -3
- package/dist/vendor/devkit/defaults-engine.js +69 -7
- package/dist/vendor/devkit/deploy-follow.js +297 -0
- package/dist/vendor/devkit/deploy-precheck.js +23 -8
- package/dist/vendor/devkit/deploy-record.js +11 -27
- package/dist/vendor/devkit/deploy-snapshot.js +661 -0
- package/dist/vendor/devkit/deploy.js +445 -75
- package/dist/vendor/devkit/git-auth.js +73 -0
- package/dist/vendor/devkit/git-remote.js +95 -0
- package/dist/vendor/devkit/github-repo.js +290 -0
- package/dist/vendor/devkit/local.js +47 -0
- package/dist/vendor/devkit/merge-line-files.js +23 -16
- package/dist/vendor/devkit/omega-bin.js +18 -3
- package/dist/vendor/devkit/pack-local.js +391 -0
- package/dist/vendor/devkit/preludes/index.js +120 -0
- package/dist/vendor/devkit/preludes/origin-heal.js +156 -0
- package/dist/vendor/devkit/service-account.js +43 -0
- package/dist/vendor/devkit/ship-plan.js +112 -0
- package/dist/vendor/devkit/signing-env.js +180 -0
- package/dist/vendor/devkit/signing-tree.js +92 -0
- package/dist/vendor/devkit/target-seams.js +142 -0
- package/dist/vendor/devkit/target-secrets.js +235 -50
- package/dist/vendor/devkit/test/esm-only-fixture.js +48 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/browser.js +19 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/index.js +20 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/package.json +13 -0
- package/dist/vendor/monitoring/env.js +20 -10
- package/dist/vendor/monitoring/main.js +1 -1
- package/dist/vendor/monitoring/preload.js +1 -1
- package/dist/vendor/monitoring/renderer.js +1 -1
- package/docs/analytics.md +1 -1
- package/docs/auto-updater.md +5 -5
- package/docs/boot-sequence.md +1 -1
- package/docs/build-system.md +15 -7
- package/docs/client-bridge.md +9 -7
- package/docs/config-schema.md +4 -4
- package/docs/css.md +8 -2
- package/docs/deep-link.md +12 -4
- package/docs/environment-detection.md +32 -24
- package/docs/hooks.md +3 -1
- package/docs/icons.md +7 -7
- package/docs/index.md +61 -23
- package/docs/installer-options.md +24 -21
- package/docs/logging.md +5 -5
- package/docs/releasing.md +25 -17
- package/docs/runner.md +40 -5
- package/docs/shared/brands.md +12 -6
- package/docs/shared/breaking-changes.md +375 -21
- package/docs/shared/config.md +757 -199
- package/docs/shared/deploys.md +194 -91
- package/docs/shared/icons.md +18 -0
- package/docs/shared/local-dev.md +24 -6
- package/docs/shared/logging.md +9 -6
- package/docs/shared/monitoring.md +27 -13
- package/docs/shared/publishing.md +19 -15
- package/docs/shared/rulings.md +2 -2
- package/docs/shared/testing.md +1 -1
- package/docs/shared/theming.md +26 -1
- package/docs/shared/translation.md +49 -7
- package/docs/shared/updates.md +3 -3
- package/docs/signing.md +59 -33
- package/docs/test-framework.md +10 -5
- package/docs/themes.md +15 -1
- package/package.json +18 -12
- package/bin/omega-desktop +0 -2
- package/dist/commands/push-secrets.js +0 -141
- package/dist/test/suites/build/deliver-certs.test.js +0 -95
- package/dist/test/suites/build/derive-signing-env.test.js +0 -122
- package/dist/test/suites/build/push-secrets.test.js +0 -226
- package/dist/test/suites/build/resolve-signing-cert.test.js +0 -342
- package/dist/utils/deliver-certs.js +0 -69
- package/dist/utils/derive-signing-env.js +0 -56
- package/dist/utils/resolve-signing-cert.js +0 -175
- package/dist/vendor/config/desktop-artifacts.js +0 -110
- package/dist/vendor/config/instances.js +0 -208
- /package/dist/defaults/config/icons/{macos → mac}/dmg.png +0 -0
- /package/dist/defaults/config/icons/{macos → mac}/icon.png +0 -0
- /package/dist/defaults/config/icons/{macos → mac}/tray.png +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Build-layer tests for lib/auth-persistence.js `resolve()`: WHICH strategy a boot
|
|
2
|
+
// settles on ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)).
|
|
3
|
+
//
|
|
4
|
+
// The managers are plain objects and `available()` is stubbed, so the production
|
|
5
|
+
// default is provable with no Electron and, above all, with no call into this box's
|
|
6
|
+
// OS keychain. The real-manager half lives in the main suite, which now runs under
|
|
7
|
+
// the forced test-mode strategy itself.
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
11
|
+
|
|
12
|
+
const authPersistence = require(path.join(__dirname, '..', '..', '..', 'lib', 'auth-persistence.js'));
|
|
13
|
+
|
|
14
|
+
// resolve() reports on the module logger; collect the lines instead of printing
|
|
15
|
+
// them into THIS run's output.
|
|
16
|
+
async function capture(lines, fn) {
|
|
17
|
+
const realLog = console.log;
|
|
18
|
+
const realWarn = console.warn;
|
|
19
|
+
console.log = (...args) => lines.push(args.join(' '));
|
|
20
|
+
console.warn = (...args) => lines.push(args.join(' '));
|
|
21
|
+
try {
|
|
22
|
+
return await fn();
|
|
23
|
+
} finally {
|
|
24
|
+
console.log = realLog;
|
|
25
|
+
console.warn = realWarn;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Stand in for the OS vault, counting every time the boot asks it anything.
|
|
30
|
+
function stubSafeStorage() {
|
|
31
|
+
const calls = { available: 0 };
|
|
32
|
+
const strategy = authPersistence._strategies.safeStorage;
|
|
33
|
+
const original = strategy.available;
|
|
34
|
+
strategy.available = async () => { calls.available += 1; return true; };
|
|
35
|
+
return { calls, restore: () => { strategy.available = original; } };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = defineCases({
|
|
39
|
+
type: 'group',
|
|
40
|
+
layer: 'build',
|
|
41
|
+
description: 'auth-persistence resolve(): the production default, and the test-mode override',
|
|
42
|
+
tests: [
|
|
43
|
+
{
|
|
44
|
+
name: 'a production manager with no config still picks safeStorage; an unknown name warns to null',
|
|
45
|
+
run: async (ctx) => {
|
|
46
|
+
const vault = stubSafeStorage();
|
|
47
|
+
const lines = [];
|
|
48
|
+
try {
|
|
49
|
+
const active = await capture(lines, () => authPersistence.resolve({ config: {}, isTesting: () => false }));
|
|
50
|
+
ctx.expect(active.name).toBe('safeStorage');
|
|
51
|
+
ctx.expect(vault.calls.available).toBe(1);
|
|
52
|
+
|
|
53
|
+
const unknown = await capture(lines, () => authPersistence.resolve({
|
|
54
|
+
config: { omega: { authPersistence: 'does-not-exist' } },
|
|
55
|
+
isTesting: () => false,
|
|
56
|
+
}));
|
|
57
|
+
ctx.expect(unknown).toBeNull();
|
|
58
|
+
ctx.expect(lines.join('\n')).toMatch(/unknown authPersistence strategy "does-not-exist"/);
|
|
59
|
+
} finally {
|
|
60
|
+
vault.restore();
|
|
61
|
+
authPersistence._active = null;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'a testing manager gets none before any config read, and the vault is never asked',
|
|
67
|
+
run: async (ctx) => {
|
|
68
|
+
const vault = stubSafeStorage();
|
|
69
|
+
const lines = [];
|
|
70
|
+
try {
|
|
71
|
+
// An explicit consumer value too: the boot layer boots the brand's REAL config.
|
|
72
|
+
const active = await capture(lines, () => authPersistence.resolve({
|
|
73
|
+
config: { omega: { authPersistence: 'safeStorage' } },
|
|
74
|
+
isTesting: () => true,
|
|
75
|
+
}));
|
|
76
|
+
ctx.expect(active).toBeNull();
|
|
77
|
+
ctx.expect(authPersistence.getActive()).toBeNull();
|
|
78
|
+
ctx.expect(vault.calls.available).toBe(0);
|
|
79
|
+
ctx.expect(lines.join('\n')).toMatch(/auth persistence: none \(test mode\)/);
|
|
80
|
+
} finally {
|
|
81
|
+
vault.restore();
|
|
82
|
+
authPersistence._active = null;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'the boot lane is a test run too: the lane variable answers, over the word baked into its production artifact',
|
|
88
|
+
run: async (ctx) => {
|
|
89
|
+
const { isTesting, ENVIRONMENT_VAR } = require('../../../vendor/config/environment.js');
|
|
90
|
+
const vault = stubSafeStorage();
|
|
91
|
+
const lines = [];
|
|
92
|
+
const previous = process.env[ENVIRONMENT_VAR];
|
|
93
|
+
try {
|
|
94
|
+
// What the boot lane actually looks like from in here: the app under test is
|
|
95
|
+
// the consumer's PRODUCTION bundle, so the config it booted from carries
|
|
96
|
+
// `production`. The runner spawned it with the one input, main.js leaves an
|
|
97
|
+
// explicit one alone ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)),
|
|
98
|
+
// and so the real isTesting() answers the lane, not the bake.
|
|
99
|
+
process.env[ENVIRONMENT_VAR] = 'testing';
|
|
100
|
+
const manager = {
|
|
101
|
+
config: { environment: 'production', omega: { authPersistence: 'safeStorage' } },
|
|
102
|
+
isTesting,
|
|
103
|
+
};
|
|
104
|
+
ctx.expect(manager.isTesting()).toBe(true);
|
|
105
|
+
|
|
106
|
+
const active = await capture(lines, () => authPersistence.resolve(manager));
|
|
107
|
+
ctx.expect(active).toBeNull();
|
|
108
|
+
ctx.expect(vault.calls.available).toBe(0);
|
|
109
|
+
ctx.expect(lines.join('\n')).toMatch(/auth persistence: none \(test mode\)/);
|
|
110
|
+
} finally {
|
|
111
|
+
if (previous === undefined) delete process.env[ENVIRONMENT_VAR];
|
|
112
|
+
else process.env[ENVIRONMENT_VAR] = previous;
|
|
113
|
+
vault.restore();
|
|
114
|
+
authPersistence._active = null;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Build-layer tests for the SafeNet PIN watcher. The desktop it types into is
|
|
2
|
+
// behind a driver seam, so what is proven here is the loop: the dialog is found,
|
|
3
|
+
// the PIN and Enter go in, and the dialog closing (or not) is reported.
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
7
|
+
|
|
8
|
+
const { startAutoUnlock } = require(path.join(__dirname, '..', '..', '..', 'lib', 'sign-helpers', 'auto-unlock.js'));
|
|
9
|
+
|
|
10
|
+
// Milliseconds, so a case is over in well under a second.
|
|
11
|
+
const FAST = { pollIntervalMs: 5, pollTimeoutMs: 500, preTypeDelayMs: 1, perCharDelayMs: 0, confirmMs: 60 };
|
|
12
|
+
|
|
13
|
+
function fakeLogger() {
|
|
14
|
+
const lines = [];
|
|
15
|
+
return {
|
|
16
|
+
lines,
|
|
17
|
+
log: (m) => lines.push(`log: ${m}`),
|
|
18
|
+
warn: (m) => lines.push(`warn: ${m}`),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function waitFor(check, ms) {
|
|
23
|
+
const deadline = Date.now() + ms;
|
|
24
|
+
while (Date.now() < deadline) {
|
|
25
|
+
if (check()) return;
|
|
26
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`waitFor: condition not met within ${ms}ms`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = defineCases({
|
|
32
|
+
type: 'group',
|
|
33
|
+
layer: 'build',
|
|
34
|
+
description: 'auto-unlock — the Token Logon watcher, typed and confirmed',
|
|
35
|
+
tests: [
|
|
36
|
+
{
|
|
37
|
+
name: 'the PIN and Enter go into the dialog, and the dialog closing is reported',
|
|
38
|
+
run: async (ctx) => {
|
|
39
|
+
const logger = fakeLogger();
|
|
40
|
+
const typed = [];
|
|
41
|
+
let open = true;
|
|
42
|
+
const driver = {
|
|
43
|
+
findTokenLogonWindow: async () => (open ? 'Token Logon' : null),
|
|
44
|
+
typeText: async (t) => typed.push(t),
|
|
45
|
+
pressEnter: async () => { typed.push('<Enter>'); open = false; },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const unlock = startAutoUnlock({ password: 'ab1', logger, driver, timings: FAST, platform: 'win32' });
|
|
49
|
+
await waitFor(() => logger.lines.some((l) => l.includes('dialog closed')), 2000);
|
|
50
|
+
unlock.stop();
|
|
51
|
+
|
|
52
|
+
ctx.expect(typed).toEqual(['a', 'b', '1', '<Enter>']);
|
|
53
|
+
ctx.expect(logger.lines.some((l) => l.includes('password typed + Enter pressed'))).toBe(true);
|
|
54
|
+
ctx.expect(logger.lines.some((l) => l.startsWith('warn:'))).toBe(false);
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'a dialog still open after typing is called out: the keystrokes did not reach it',
|
|
59
|
+
run: async (ctx) => {
|
|
60
|
+
// A locked console does exactly this ([#864](https://github.com/Omega-JS-Stack/omega/issues/864)):
|
|
61
|
+
// the window is enumerable, the keys land on the lock screen.
|
|
62
|
+
const logger = fakeLogger();
|
|
63
|
+
const driver = {
|
|
64
|
+
findTokenLogonWindow: async () => 'Token Logon',
|
|
65
|
+
typeText: async () => {},
|
|
66
|
+
pressEnter: async () => {},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const unlock = startAutoUnlock({ password: 'ab1', logger, driver, timings: FAST, platform: 'win32' });
|
|
70
|
+
await waitFor(() => logger.lines.some((l) => l.includes('still open')), 2000);
|
|
71
|
+
unlock.stop();
|
|
72
|
+
|
|
73
|
+
const warning = logger.lines.find((l) => l.includes('still open'));
|
|
74
|
+
ctx.expect(warning.startsWith('warn:')).toBe(true);
|
|
75
|
+
ctx.expect(warning).toMatch(/keystrokes did not reach it/);
|
|
76
|
+
ctx.expect(warning).toMatch(/console session locked/);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'no dialog within the poll window is a warning, and stop() ends the watch silently',
|
|
81
|
+
run: async (ctx) => {
|
|
82
|
+
const logger = fakeLogger();
|
|
83
|
+
const driver = {
|
|
84
|
+
findTokenLogonWindow: async () => null,
|
|
85
|
+
typeText: async () => { throw new Error('nothing should be typed'); },
|
|
86
|
+
pressEnter: async () => { throw new Error('nothing should be typed'); },
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
startAutoUnlock({ password: 'ab1', logger, driver, timings: FAST, platform: 'win32' });
|
|
90
|
+
await waitFor(() => logger.lines.some((l) => l.includes('timed out')), 2000);
|
|
91
|
+
|
|
92
|
+
// Stopped watchers say nothing more: signtool finished on its own.
|
|
93
|
+
const quiet = fakeLogger();
|
|
94
|
+
const unlock = startAutoUnlock({ password: 'ab1', logger: quiet, driver, timings: FAST, platform: 'win32' });
|
|
95
|
+
unlock.stop();
|
|
96
|
+
await new Promise((r) => setTimeout(r, FAST.pollTimeoutMs + 50));
|
|
97
|
+
ctx.expect(quiet.lines.some((l) => l.startsWith('warn:'))).toBe(false);
|
|
98
|
+
|
|
99
|
+
// Off Windows, or with no PIN, the watcher is a no-op.
|
|
100
|
+
ctx.expect(typeof startAutoUnlock({ password: 'x', driver, platform: 'darwin' }).stop).toBe('function');
|
|
101
|
+
ctx.expect(typeof startAutoUnlock({ password: '', driver, platform: 'win32' }).stop).toBe('function');
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// Build-layer tests for the boot runner's budget
|
|
2
|
+
// ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)).
|
|
3
|
+
//
|
|
4
|
+
// A boot that blocks inside initialize() never reaches the harness, so the per-test
|
|
5
|
+
// timeout in harness/boot-entry.js cannot fire and the spawn (which only resolved on
|
|
6
|
+
// exit) hung `npx omega test` forever. The child is INJECTED here, so both verdicts
|
|
7
|
+
// are provable without booting Electron: a child that never exits, and one that exits
|
|
8
|
+
// the way a healthy run does.
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const { EventEmitter } = require('events');
|
|
14
|
+
const jetpack = require('fs-jetpack');
|
|
15
|
+
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
16
|
+
|
|
17
|
+
const { runBootChild } = require(path.join(__dirname, '..', '..', 'runners', 'boot.js'));
|
|
18
|
+
|
|
19
|
+
// A project root the runner reads its report clue from. `logLine` null writes no
|
|
20
|
+
// logs/ at all, the boot that died before the logger ever opened a file.
|
|
21
|
+
function seedRoot(logLine) {
|
|
22
|
+
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-boot-budget-')));
|
|
23
|
+
if (logLine !== null) {
|
|
24
|
+
// A trailing blank line is what a log file actually ends with.
|
|
25
|
+
jetpack.write(path.join(root, 'logs', 'runtime.log'), `first line\n${logLine}\n\n`);
|
|
26
|
+
}
|
|
27
|
+
return root;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// The spawn seam's child: the surface runBootChild touches, and a record of every
|
|
31
|
+
// signal it was sent.
|
|
32
|
+
function fakeChild() {
|
|
33
|
+
const child = new EventEmitter();
|
|
34
|
+
child.stdout = new EventEmitter();
|
|
35
|
+
child.stderr = new EventEmitter();
|
|
36
|
+
child.signals = [];
|
|
37
|
+
child.kill = (signal) => { child.signals.push(signal || 'SIGTERM'); return true; };
|
|
38
|
+
return child;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The runner reports on console.log; collect it instead of printing it into THIS run.
|
|
42
|
+
async function capture(lines, fn) {
|
|
43
|
+
const realLog = console.log;
|
|
44
|
+
console.log = (line) => lines.push(String(line));
|
|
45
|
+
try {
|
|
46
|
+
return await fn();
|
|
47
|
+
} finally {
|
|
48
|
+
console.log = realLog;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function sleep(ms) {
|
|
53
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = defineCases({
|
|
57
|
+
type: 'group',
|
|
58
|
+
layer: 'build',
|
|
59
|
+
description: 'boot runner budget: a boot with no harness output fails with a report instead of hanging',
|
|
60
|
+
tests: [
|
|
61
|
+
{
|
|
62
|
+
name: 'the budget fires: the report quotes the last runtime.log line, every boot test counts failed, the child is ended',
|
|
63
|
+
run: async (ctx) => {
|
|
64
|
+
const root = seedRoot('[@omega.js/desktop:startup] isLaunchHidden: true');
|
|
65
|
+
const specFile = path.join(root, 'spec.json');
|
|
66
|
+
jetpack.write(specFile, '{}');
|
|
67
|
+
const child = fakeChild();
|
|
68
|
+
const lines = [];
|
|
69
|
+
const started = Date.now();
|
|
70
|
+
|
|
71
|
+
const counts = await capture(lines, () => runBootChild({
|
|
72
|
+
electronBin: '/not/spawned/electron',
|
|
73
|
+
args: [root],
|
|
74
|
+
childEnv: {},
|
|
75
|
+
effectiveRoot: root,
|
|
76
|
+
specFile,
|
|
77
|
+
skipEvents: [],
|
|
78
|
+
testCount: 3,
|
|
79
|
+
spawnFn: () => child,
|
|
80
|
+
timeoutMs: 1000,
|
|
81
|
+
killGraceMs: 20,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
// Resolved on its own timer, not at the child's leisure.
|
|
85
|
+
ctx.expect(Date.now() - started).toBeLessThan(5000);
|
|
86
|
+
ctx.expect(counts).toEqual({ passed: 0, failed: 3, skipped: 0 });
|
|
87
|
+
|
|
88
|
+
const report = lines.join('\n');
|
|
89
|
+
ctx.expect(report).toMatch(/✗ boot: no harness output after 1s/);
|
|
90
|
+
ctx.expect(report).toMatch(/last runtime\.log line: "\[@omega\.js\/desktop:startup\] isLaunchHidden: true"/);
|
|
91
|
+
|
|
92
|
+
// The child is ended, and escalated when the grace passes without it going.
|
|
93
|
+
ctx.expect(child.signals).toEqual(['SIGTERM']);
|
|
94
|
+
await sleep(60);
|
|
95
|
+
ctx.expect(child.signals).toEqual(['SIGTERM', 'SIGKILL']);
|
|
96
|
+
|
|
97
|
+
ctx.expect(fs.existsSync(specFile)).toBe(false);
|
|
98
|
+
jetpack.remove(root);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'a missing runtime.log is reported as such, not a crash',
|
|
103
|
+
run: async (ctx) => {
|
|
104
|
+
const root = seedRoot(null);
|
|
105
|
+
const specFile = path.join(root, 'spec.json');
|
|
106
|
+
jetpack.write(specFile, '{}');
|
|
107
|
+
const child = fakeChild();
|
|
108
|
+
const lines = [];
|
|
109
|
+
|
|
110
|
+
const counts = await capture(lines, () => runBootChild({
|
|
111
|
+
electronBin: '/not/spawned/electron',
|
|
112
|
+
args: [root],
|
|
113
|
+
childEnv: {},
|
|
114
|
+
effectiveRoot: root,
|
|
115
|
+
specFile,
|
|
116
|
+
skipEvents: [],
|
|
117
|
+
testCount: 1,
|
|
118
|
+
spawnFn: () => child,
|
|
119
|
+
timeoutMs: 20,
|
|
120
|
+
killGraceMs: 20,
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
ctx.expect(counts.failed).toBe(1);
|
|
124
|
+
ctx.expect(lines.join('\n')).toMatch(/last runtime\.log line: "\(no runtime\.log\)"/);
|
|
125
|
+
jetpack.remove(root);
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
// The budget is IDLE time, never total run time: a boot suite that
|
|
130
|
+
// legitimately runs longer than the budget keeps going as long as it
|
|
131
|
+
// keeps talking, and only SILENCE for a whole budget fails the run.
|
|
132
|
+
name: 'harness output REARMS the budget, so a run past it is never killed',
|
|
133
|
+
run: async (ctx) => {
|
|
134
|
+
const root = seedRoot('[@omega.js/desktop:startup] isLaunchHidden: true');
|
|
135
|
+
const specFile = path.join(root, 'spec.json');
|
|
136
|
+
jetpack.write(specFile, '{}');
|
|
137
|
+
const child = fakeChild();
|
|
138
|
+
const lines = [];
|
|
139
|
+
const started = Date.now();
|
|
140
|
+
|
|
141
|
+
const counts = await capture(lines, async () => {
|
|
142
|
+
const pending = runBootChild({
|
|
143
|
+
electronBin: '/not/spawned/electron',
|
|
144
|
+
args: [root],
|
|
145
|
+
childEnv: {},
|
|
146
|
+
effectiveRoot: root,
|
|
147
|
+
specFile,
|
|
148
|
+
skipEvents: [],
|
|
149
|
+
testCount: 2,
|
|
150
|
+
spawnFn: () => child,
|
|
151
|
+
timeoutMs: 400,
|
|
152
|
+
killGraceMs: 20,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
child.stdout.emit('data', `__EM_TEST__${JSON.stringify({ event: 'result', name: 'the app boots', passed: true, duration: 12 })}\n`);
|
|
156
|
+
await sleep(150);
|
|
157
|
+
child.stdout.emit('data', `__EM_TEST__${JSON.stringify({ event: 'result', name: 'a slow suite finishes', passed: true, duration: 12 })}\n`);
|
|
158
|
+
await sleep(150);
|
|
159
|
+
child.stdout.emit('data', `__EM_TEST__${JSON.stringify({ event: 'result', name: 'and another', passed: true, duration: 12 })}\n`);
|
|
160
|
+
await sleep(150);
|
|
161
|
+
child.emit('exit', 0);
|
|
162
|
+
|
|
163
|
+
return pending;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
ctx.expect(Date.now() - started).toBeGreaterThan(400);
|
|
167
|
+
ctx.expect(counts).toEqual({ passed: 3, failed: 0, skipped: 0 });
|
|
168
|
+
ctx.expect(child.signals).toEqual([]);
|
|
169
|
+
ctx.expect(lines.join('\n').includes('no harness output')).toBe(false);
|
|
170
|
+
jetpack.remove(root);
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
// The timeout hands its counts to the caller, so a line arriving while
|
|
175
|
+
// the child is being killed must reach nothing at all: the report is
|
|
176
|
+
// already printed and the numbers already reported.
|
|
177
|
+
name: 'a line landing AFTER the timeout report changes nothing',
|
|
178
|
+
run: async (ctx) => {
|
|
179
|
+
const root = seedRoot('[@omega.js/desktop:startup] isLaunchHidden: true');
|
|
180
|
+
const specFile = path.join(root, 'spec.json');
|
|
181
|
+
jetpack.write(specFile, '{}');
|
|
182
|
+
const child = fakeChild();
|
|
183
|
+
const lines = [];
|
|
184
|
+
|
|
185
|
+
const counts = await capture(lines, async () => {
|
|
186
|
+
const settled = await runBootChild({
|
|
187
|
+
electronBin: '/not/spawned/electron',
|
|
188
|
+
args: [root],
|
|
189
|
+
childEnv: {},
|
|
190
|
+
effectiveRoot: root,
|
|
191
|
+
specFile,
|
|
192
|
+
skipEvents: [],
|
|
193
|
+
testCount: 2,
|
|
194
|
+
spawnFn: () => child,
|
|
195
|
+
timeoutMs: 30,
|
|
196
|
+
killGraceMs: 20,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
child.stdout.emit('data', `__EM_TEST__${JSON.stringify({ event: 'result', name: 'a late line', passed: true, duration: 12 })}\n`);
|
|
200
|
+
child.emit('exit', 0);
|
|
201
|
+
await sleep(40);
|
|
202
|
+
|
|
203
|
+
return settled;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
ctx.expect(counts).toEqual({ passed: 0, failed: 2, skipped: 0 });
|
|
207
|
+
ctx.expect(lines.join('\n').includes('a late line')).toBe(false);
|
|
208
|
+
jetpack.remove(root);
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'a child that exits normally reports its results and clears the timer',
|
|
213
|
+
run: async (ctx) => {
|
|
214
|
+
const root = seedRoot('[@omega.js/desktop:startup] isLaunchHidden: true');
|
|
215
|
+
const specFile = path.join(root, 'spec.json');
|
|
216
|
+
jetpack.write(specFile, '{}');
|
|
217
|
+
const child = fakeChild();
|
|
218
|
+
const lines = [];
|
|
219
|
+
|
|
220
|
+
const counts = await capture(lines, async () => {
|
|
221
|
+
const pending = runBootChild({
|
|
222
|
+
electronBin: '/not/spawned/electron',
|
|
223
|
+
args: [root],
|
|
224
|
+
childEnv: {},
|
|
225
|
+
effectiveRoot: root,
|
|
226
|
+
specFile,
|
|
227
|
+
skipEvents: [],
|
|
228
|
+
testCount: 1,
|
|
229
|
+
spawnFn: () => child,
|
|
230
|
+
timeoutMs: 50,
|
|
231
|
+
killGraceMs: 20,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
child.stdout.emit('data', `__EM_TEST__${JSON.stringify({ event: 'result', name: 'the app boots', passed: true, duration: 12 })}\n`);
|
|
235
|
+
child.emit('exit', 0);
|
|
236
|
+
|
|
237
|
+
const settled = await pending;
|
|
238
|
+
// Past the budget: a timer the exit did not clear would report here.
|
|
239
|
+
await sleep(120);
|
|
240
|
+
return settled;
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
ctx.expect(counts).toEqual({ passed: 1, failed: 0, skipped: 0 });
|
|
244
|
+
ctx.expect(child.signals).toEqual([]);
|
|
245
|
+
|
|
246
|
+
const report = lines.join('\n');
|
|
247
|
+
ctx.expect(report).toMatch(/✓ the app boots/);
|
|
248
|
+
ctx.expect(report.includes('no harness output')).toBe(false);
|
|
249
|
+
jetpack.remove(root);
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Build-layer proof for the generated brand partial
|
|
2
|
+
// ([#912](https://github.com/Omega-JS-Stack/omega/issues/912)): ONE hex in
|
|
3
|
+
// `brand.color` decides both the compile-time accent Bootstrap derives from and
|
|
4
|
+
// the runtime --omega-accent ramp, on a real fixture project through the real
|
|
5
|
+
// sass task.
|
|
6
|
+
//
|
|
7
|
+
// Offline by construction: a temp consumer dir with a config and the scaffold's
|
|
8
|
+
// own main.scss, compiled against this package's dist assets.
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const jetpack = require('fs-jetpack');
|
|
14
|
+
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
15
|
+
|
|
16
|
+
const SRC = path.join(__dirname, '..', '..', '..');
|
|
17
|
+
const SCAFFOLD_MAIN = path.join(SRC, 'defaults', 'src', 'assets', 'scss', 'main.scss');
|
|
18
|
+
|
|
19
|
+
/** A consumer whose main.scss is the one the scaffold ships. */
|
|
20
|
+
function stageConsumer(color) {
|
|
21
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-brand-scss-'));
|
|
22
|
+
const brand = color ? `{ id: "fixture", color: "${color}" }` : '{ id: "fixture" }';
|
|
23
|
+
|
|
24
|
+
jetpack.write(path.join(tmp, 'config', 'omega.json5'), `{ brand: ${brand}, targets: { desktop: {} } }`);
|
|
25
|
+
jetpack.copy(SCAFFOLD_MAIN, path.join(tmp, 'src', 'assets', 'scss', 'main.scss'));
|
|
26
|
+
|
|
27
|
+
return tmp;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Run the sass task with the fixture as the project root. Both the task and the
|
|
32
|
+
* build Manager resolve that root at REQUIRE time, so each run gets a fresh
|
|
33
|
+
* copy of both.
|
|
34
|
+
*/
|
|
35
|
+
function runSassIn(dir) {
|
|
36
|
+
const oldCwd = process.cwd();
|
|
37
|
+
for (const key of Object.keys(require.cache)) {
|
|
38
|
+
if (key.includes(`${path.sep}build.js`) || key.endsWith(`tasks${path.sep}sass.js`)) delete require.cache[key];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
process.chdir(dir);
|
|
43
|
+
const sassTask = require(path.join(SRC, 'gulp', 'tasks', 'sass.js'));
|
|
44
|
+
return new Promise((resolve, reject) => sassTask((error) => (error ? reject(error) : resolve())));
|
|
45
|
+
} finally {
|
|
46
|
+
process.chdir(oldCwd);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = defineCases({
|
|
51
|
+
type: 'suite',
|
|
52
|
+
layer: 'build',
|
|
53
|
+
description: 'brand scss (#912): brand.color drives $primary and the runtime accent ramp',
|
|
54
|
+
tests: [
|
|
55
|
+
{
|
|
56
|
+
name: 'brand.color writes the partial and paints the compiled bundle',
|
|
57
|
+
run: async (ctx) => {
|
|
58
|
+
const dir = stageConsumer('#ff0066');
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await runSassIn(dir);
|
|
62
|
+
|
|
63
|
+
const partial = fs.readFileSync(path.join(dir, 'dist', 'assets', 'scss', '_brand.scss'), 'utf8');
|
|
64
|
+
ctx.expect(partial.includes('$primary: #ff0066;')).toBe(true);
|
|
65
|
+
|
|
66
|
+
const css = fs.readFileSync(path.join(dir, 'dist', 'assets', 'css', 'main.bundle.css'), 'utf8');
|
|
67
|
+
|
|
68
|
+
// Bootstrap's primary compiled from the same hex...
|
|
69
|
+
ctx.expect(css.includes('--bs-primary: #ff0066')).toBe(true);
|
|
70
|
+
|
|
71
|
+
// ...and the runtime ramp, AFTER the token sheet's placeholder so it
|
|
72
|
+
// wins the cascade (a used module's css emits at its load position,
|
|
73
|
+
// which is why the scaffold includes the mixin instead).
|
|
74
|
+
ctx.expect(css.includes('--omega-accent: #ff0066')).toBe(true);
|
|
75
|
+
ctx.expect(css.includes('--omega-accent-hover: #db0058')).toBe(true);
|
|
76
|
+
ctx.expect(css.lastIndexOf('--omega-accent: #ff0066') > css.lastIndexOf('--omega-accent: #2563eb')).toBe(true);
|
|
77
|
+
|
|
78
|
+
// The dark variant rides the same stamps as the token sheet.
|
|
79
|
+
ctx.expect(/@media \(prefers-color-scheme: dark\)[\s\S]*?--omega-accent: #ff3385/.test(css)).toBe(true);
|
|
80
|
+
ctx.expect(/\[data-bs-theme=["']?dark["']?\][\s\S]*?--omega-accent: #ff3385/.test(css)).toBe(true);
|
|
81
|
+
} finally {
|
|
82
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'no brand.color compiles on the framework default',
|
|
88
|
+
run: async (ctx) => {
|
|
89
|
+
const dir = stageConsumer(null);
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await runSassIn(dir);
|
|
93
|
+
|
|
94
|
+
const partial = fs.readFileSync(path.join(dir, 'dist', 'assets', 'scss', '_brand.scss'), 'utf8');
|
|
95
|
+
ctx.expect(partial.includes('$primary: #2563eb;')).toBe(true);
|
|
96
|
+
|
|
97
|
+
const css = fs.readFileSync(path.join(dir, 'dist', 'assets', 'css', 'main.bundle.css'), 'utf8');
|
|
98
|
+
ctx.expect(css.includes('--bs-primary: #2563eb')).toBe(true);
|
|
99
|
+
ctx.expect(css.includes('--omega-accent: #2563eb')).toBe(true);
|
|
100
|
+
} finally {
|
|
101
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
});
|