forked from wangziqi/gongxue-base
140 lines
5.8 KiB
JavaScript
140 lines
5.8 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import {
|
|
enforceTaroH5RuntimePatches,
|
|
replaceExactlyOnce,
|
|
taroButtonLoadingPatch,
|
|
taroH5RuntimePatchDefinition,
|
|
taroInputWatcherPatch,
|
|
} from './taro-components-h5-runtime-patch.js';
|
|
|
|
const repoRoot = process.cwd();
|
|
|
|
function writeJson(filePath, value) {
|
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
}
|
|
|
|
function installedPristineSource(patch) {
|
|
const target = path.join(repoRoot, 'node_modules', '@tarojs', 'components', patch.targetRelativePath);
|
|
const installed = fs.readFileSync(target, 'utf8');
|
|
const pristine = installed.includes(patch.after) ? installed.replace(patch.after, patch.before) : installed;
|
|
return pristine;
|
|
}
|
|
|
|
const pristineSources = new Map([
|
|
[taroInputWatcherPatch.id, installedPristineSource(taroInputWatcherPatch)],
|
|
[taroButtonLoadingPatch.id, installedPristineSource(taroButtonLoadingPatch)],
|
|
]);
|
|
|
|
function createFixture(options = {}) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-taro-h5-runtime-patch-'));
|
|
const taroManifest = {
|
|
name: '@tiku-saas/taro',
|
|
version: '0.1.0',
|
|
scripts: { postinstall: taroH5RuntimePatchDefinition.postinstallCommand },
|
|
devDependencies: {
|
|
'@tarojs/components': options.declaredVersion || taroH5RuntimePatchDefinition.packageVersion,
|
|
},
|
|
};
|
|
writeJson(path.join(root, 'apps', 'taro', 'package.json'), taroManifest);
|
|
writeJson(path.join(root, 'package-lock.json'), {
|
|
lockfileVersion: 3,
|
|
packages: {
|
|
'apps/taro': {
|
|
hasInstallScript: true,
|
|
devDependencies: {
|
|
'@tarojs/components': options.lockWorkspaceVersion || taroH5RuntimePatchDefinition.packageVersion,
|
|
},
|
|
},
|
|
'node_modules/@tarojs/components': {
|
|
version: options.lockVersion || taroH5RuntimePatchDefinition.packageVersion,
|
|
integrity: options.integrity || taroH5RuntimePatchDefinition.lockIntegrity,
|
|
},
|
|
},
|
|
});
|
|
writeJson(path.join(root, 'node_modules', '@tarojs', 'components', 'package.json'), {
|
|
name: '@tarojs/components',
|
|
version: options.installedVersion || taroH5RuntimePatchDefinition.packageVersion,
|
|
});
|
|
|
|
const targets = new Map();
|
|
for (const patch of taroH5RuntimePatchDefinition.patches) {
|
|
const target = path.join(root, 'node_modules', '@tarojs', 'components', patch.targetRelativePath);
|
|
targets.set(patch.id, target);
|
|
if (options.missingTarget !== patch.id) {
|
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
fs.writeFileSync(
|
|
target,
|
|
options.sources?.[patch.id] ?? pristineSources.get(patch.id),
|
|
'utf8',
|
|
);
|
|
}
|
|
}
|
|
return { root, targets };
|
|
}
|
|
|
|
function targetContents(fixture) {
|
|
return Object.fromEntries([...fixture.targets].map(([id, target]) => [
|
|
id,
|
|
fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : null,
|
|
]));
|
|
}
|
|
|
|
const workingState = enforceTaroH5RuntimePatches({ root: repoRoot, mode: 'check' });
|
|
assert.equal(workingState.status, 'pass');
|
|
assert.equal(workingState.patches.inputWatcher.installedSha256, taroInputWatcherPatch.patchedSha256);
|
|
assert.equal(workingState.patches.buttonLoading.installedSha256, taroButtonLoadingPatch.patchedSha256);
|
|
|
|
const applied = createFixture();
|
|
try {
|
|
const first = enforceTaroH5RuntimePatches({ root: applied.root, mode: 'apply' });
|
|
assert.equal(first.patches.inputWatcher.state, 'patched-now');
|
|
assert.equal(first.patches.buttonLoading.state, 'patched-now');
|
|
const second = enforceTaroH5RuntimePatches({ root: applied.root, mode: 'apply' });
|
|
assert.equal(second.patches.inputWatcher.state, 'patched');
|
|
assert.equal(second.patches.buttonLoading.state, 'patched');
|
|
assert.equal(enforceTaroH5RuntimePatches({ root: applied.root, mode: 'check' }).status, 'pass');
|
|
} finally {
|
|
fs.rmSync(applied.root, { recursive: true, force: true });
|
|
}
|
|
|
|
const pristineCheck = createFixture();
|
|
try {
|
|
const before = targetContents(pristineCheck);
|
|
assert.throws(
|
|
() => enforceTaroH5RuntimePatches({ root: pristineCheck.root, mode: 'check' }),
|
|
/patch is not applied/,
|
|
);
|
|
assert.deepEqual(targetContents(pristineCheck), before, '--check must never modify a pristine install');
|
|
} finally {
|
|
fs.rmSync(pristineCheck.root, { recursive: true, force: true });
|
|
}
|
|
|
|
for (const [name, options, pattern] of [
|
|
['declared version', { declaredVersion: '4.2.1' }, /must pin/],
|
|
['lock integrity', { integrity: 'sha512-unreviewed' }, /unexpected.*integrity/],
|
|
['installed version', { installedVersion: '4.2.1-beta.2' }, /does not match/],
|
|
['missing Input target', { missingTarget: taroInputWatcherPatch.id }, /target is missing/],
|
|
['missing Button target', { missingTarget: taroButtonLoadingPatch.id }, /target is missing/],
|
|
['unknown Input content', { sources: { [taroInputWatcherPatch.id]: `${pristineSources.get(taroInputWatcherPatch.id)}\n// tampered\n` } }, /unreviewed.*hash/],
|
|
['unknown Button content', { sources: { [taroButtonLoadingPatch.id]: `${pristineSources.get(taroButtonLoadingPatch.id)}\n// tampered\n` } }, /unreviewed.*hash/],
|
|
]) {
|
|
const fixture = createFixture(options);
|
|
try {
|
|
const before = targetContents(fixture);
|
|
assert.throws(() => enforceTaroH5RuntimePatches({ root: fixture.root, mode: 'apply' }), pattern, name);
|
|
assert.deepEqual(targetContents(fixture), before, `${name} failure must not modify either target`);
|
|
} finally {
|
|
fs.rmSync(fixture.root, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
assert.throws(() => replaceExactlyOnce('safe', 'unsafe', 'guarded'), /found 0/);
|
|
assert.throws(() => replaceExactlyOnce('unsafe unsafe', 'unsafe', 'guarded'), /found 2/);
|
|
assert.equal(replaceExactlyOnce('before unsafe after', 'unsafe', 'guarded'), 'before guarded after');
|
|
|
|
console.log('[PASS] Taro H5 runtime patch contract');
|