Files
maka/scripts/verify-packaged-app-icons.test.mjs
agent-radar 7be535059e
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 19:05:14 +08:00

125 lines
5.0 KiB
JavaScript

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import assert from 'node:assert/strict';
import { readFile, readdir, stat } from 'node:fs/promises';
import { join } from 'node:path';
import { test } from 'node:test';
import { assertPackagedResources } from './verify-packaged-app.mjs';
/**
* Artwork is read at runtime, and Electron reports a missing file as an EMPTY
* image rather than an error — a packaging change that dropped it would ship a
* blank dock tile with nothing failing. So the packaged-resource check has to
* name every icon, and these prove it would notice.
*
* Nothing here imports build output: this file and the verifier it covers must
* load in a clean checkout, which is exactly what `check:release` gives them.
* That the shipped catalog matches the ids the app offers is the desktop
* suite's job, where `APP_ICONS` is walked against the same directory.
*/
const ART_DIRECTORY = new URL('../apps/desktop/assets/app-icons/', import.meta.url);
function recorder(missing = new Set()) {
const asked = [];
return {
asked,
requirePath: async (path) => {
asked.push(path);
if ([...missing].some((suffix) => path.endsWith(suffix))) {
throw new Error(`missing ${path}`);
}
},
forbidPath: async () => {},
};
}
test('every shipped icon file is required in a packaged build', async () => {
const artwork = (await readdir(ART_DIRECTORY)).filter((name) => name.endsWith('.png'));
assert.ok(artwork.length > 0, 'the picker ships artwork; this fixture is not optional');
const probe = recorder();
await assertPackagedResources('/Resources', probe);
for (const name of artwork) {
assert.ok(
probe.asked.includes(join('/Resources', 'assets', 'app-icons', name)),
`${name} ships in the repo but is not required in the package`,
);
}
});
/**
* The upgrade lifecycle verifies the PREVIOUSLY released installer, which
* predates `assets/` being packaged at all. Requiring the catalog there would
* fail an upgrade check over an artifact that was correct when it shipped, so
* the requirement belongs to the current contract only.
*/
test('a legacy baseline is not asked for artwork that postdates it', async () => {
const probe = recorder();
// The canonical icon rides its own flag, added for the same reason; a legacy
// baseline predates both.
await assertPackagedResources('/Resources', {
...probe,
requireAppIconCatalog: false,
requireCanonicalIcon: false,
});
const assets = probe.asked.filter((path) => path.includes('assets'));
assert.deepEqual(assets, [], `legacy baseline asked for ${assets.join(', ')}`);
// It still verifies everything the old contract did carry.
assert.ok(probe.asked.includes(join('/Resources', 'app.asar')));
});
test('a package that dropped the artwork fails the check', async () => {
const [first] = (await readdir(ART_DIRECTORY)).filter((name) => name.endsWith('.png'));
for (const dropped of [join('assets', 'app-icons', first)]) {
await assert.rejects(
assertPackagedResources('/Resources', recorder(new Set([dropped]))),
/missing/,
`dropping ${dropped} should fail`,
);
}
});
/**
* The bundle icon is drawn by Finder, Launchpad and the installer — surfaces
* that never run our code and so cannot follow a user's choice. It therefore
* has to be the shipped default, and the packaging config has to name that
* file by path because it is read before the workspace is built. This is what
* keeps the hardcoded path honest: change `DEFAULT_APP_ICON` without changing
* the packaging config and the bundle silently keeps shipping the old mark.
*/
test('the packaged bundle icon is the shipped default', async () => {
const { DEFAULT_APP_ICON } = await import('@maka/core/settings');
const config = await readFile(
new URL('../apps/desktop/electron-builder.config.mjs', import.meta.url),
'utf8',
);
const expected = `assets/app-icons/${DEFAULT_APP_ICON}.png`;
const named = [...config.matchAll(/^\s*icon: '([^']+)',/gm)].map((m) => m[1]);
assert.ok(named.length >= 2, `expected mac and win icons, found ${named.length}`);
for (const path of named) {
assert.equal(path, expected);
}
// …and the file it names has to exist, or packaging fails far from here.
await stat(new URL(`../apps/desktop/${expected}`, import.meta.url));
});