接口开始5%
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { validateAssetBuildManifest } from '../scripts/asset-build-manifest.mjs'
|
||||
|
||||
const testDirectory = path.dirname(fileURLToPath(import.meta.url))
|
||||
const workspace = path.resolve(testDirectory, '..', '..')
|
||||
|
||||
const baseAsset = () => ({
|
||||
id: 'test-asset',
|
||||
source: 'docs/design/assets/source.png',
|
||||
output: 'static/assets/output.png',
|
||||
sourcePixels: { width: 1536, height: 3840 },
|
||||
outputPixels: { width: 1440, height: 3600 },
|
||||
quality: { maxBytes: 7000000, colorSpace: 'sRGB' },
|
||||
})
|
||||
|
||||
// schema v3 以 processing.mode 为严格判别字段。每个分支只允许自身真正消费的
|
||||
// 参数,避免给不透明背景伪造透明边、色键或切片字段来“凑齐”旧结构。
|
||||
const chromaAsset = () => ({
|
||||
...baseAsset(),
|
||||
alpha: { required: true, transparentOuterPadding: 6, cornerMaxAlpha: 0 },
|
||||
edge: { forbidChromaResidue: true, forbidLightFringe: true, premultipliedAlphaCheck: true },
|
||||
processing: {
|
||||
mode: 'chroma-stretch',
|
||||
capWidth: 380,
|
||||
keyColor: '#00FF00',
|
||||
keyTolerance: 96,
|
||||
},
|
||||
})
|
||||
|
||||
const opaqueResizeAsset = () => ({
|
||||
...baseAsset(),
|
||||
processing: { mode: 'opaque-resize', resample: 'lanczos', outputMode: 'RGB' },
|
||||
})
|
||||
|
||||
const opaqueCoverAsset = () => ({
|
||||
...baseAsset(),
|
||||
processing: {
|
||||
mode: 'opaque-cover-crop',
|
||||
resample: 'lanczos',
|
||||
anchor: 'center',
|
||||
outputMode: 'RGB',
|
||||
},
|
||||
})
|
||||
|
||||
const warmFrameAsset = () => ({
|
||||
...baseAsset(),
|
||||
alpha: { required: true, transparentOuterPadding: 0, cornerMaxAlpha: 0 },
|
||||
edge: { forbidChromaResidue: false, forbidLightFringe: false, premultipliedAlphaCheck: true },
|
||||
processing: {
|
||||
mode: 'warm-gold-frame-extract',
|
||||
borderBand: 110,
|
||||
redGreenMin: 15,
|
||||
greenBlueMin: 12,
|
||||
redBlueMin: 35,
|
||||
redMaxExclusive: 245,
|
||||
blueMaxExclusive: 180,
|
||||
alphaOffset: 25,
|
||||
alphaScale: 6,
|
||||
outputMode: 'RGBA',
|
||||
},
|
||||
})
|
||||
|
||||
const manifestWith = (asset) => ({
|
||||
schemaVersion: 3,
|
||||
kind: 'asset-build-manifest',
|
||||
family: 'test-family-v3',
|
||||
assets: [asset],
|
||||
})
|
||||
|
||||
test('接受四种职责严格分离的 schema v3 处理模式', () => {
|
||||
for (const factory of [chromaAsset, opaqueResizeAsset, opaqueCoverAsset, warmFrameAsset]) {
|
||||
const manifest = manifestWith(factory())
|
||||
assert.equal(validateAssetBuildManifest(manifest, workspace), manifest)
|
||||
}
|
||||
})
|
||||
|
||||
test('拒绝没有 processing.mode 的旧 schema v3 形状', () => {
|
||||
const asset = chromaAsset()
|
||||
delete asset.processing.mode
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(asset), workspace), /processing\.mode/i)
|
||||
})
|
||||
|
||||
test('不透明分支拒绝透明度、边缘和其他模式的参数', () => {
|
||||
const withAlpha = opaqueResizeAsset()
|
||||
withAlpha.alpha = chromaAsset().alpha
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(withAlpha), workspace), /unknown field/i)
|
||||
|
||||
const withAnchor = opaqueResizeAsset()
|
||||
withAnchor.processing.anchor = 'center'
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(withAnchor), workspace), /unknown field/i)
|
||||
|
||||
const withColorKey = opaqueCoverAsset()
|
||||
withColorKey.processing.keyColor = '#00FF00'
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(withColorKey), workspace), /unknown field/i)
|
||||
})
|
||||
|
||||
test('透明分支拒绝缺失 alpha/edge 及不属于自身的处理字段', () => {
|
||||
const missingAlpha = chromaAsset()
|
||||
delete missingAlpha.alpha
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(missingAlpha), workspace), /alpha/i)
|
||||
|
||||
const missingEdge = warmFrameAsset()
|
||||
delete missingEdge.edge
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(missingEdge), workspace), /edge/i)
|
||||
|
||||
const wrongProcessing = warmFrameAsset()
|
||||
wrongProcessing.processing.capWidth = 380
|
||||
assert.throws(() => validateAssetBuildManifest(manifestWith(wrongProcessing), workspace), /unknown field/i)
|
||||
})
|
||||
|
||||
test('拒绝重复输出、越界路径、未知字段和未知模式', () => {
|
||||
const duplicate = manifestWith(chromaAsset())
|
||||
duplicate.assets.push({ ...chromaAsset(), id: 'duplicate-output' })
|
||||
assert.throws(() => validateAssetBuildManifest(duplicate, workspace), /duplicate output/i)
|
||||
|
||||
const escaped = manifestWith(opaqueResizeAsset())
|
||||
escaped.assets[0].output = '../outside.png'
|
||||
assert.throws(() => validateAssetBuildManifest(escaped, workspace), /escapes workspace/i)
|
||||
|
||||
const unknownField = manifestWith(opaqueResizeAsset())
|
||||
unknownField.assets[0].logicalSlot = 'page'
|
||||
assert.throws(() => validateAssetBuildManifest(unknownField, workspace), /unknown field/i)
|
||||
|
||||
const unknownMode = manifestWith(opaqueResizeAsset())
|
||||
unknownMode.assets[0].processing.mode = 'future-magic'
|
||||
assert.throws(() => validateAssetBuildManifest(unknownMode, workspace), /unsupported processing\.mode/i)
|
||||
})
|
||||
Reference in New Issue
Block a user