diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..34cbd89f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/source-map-tests"] + path = test/source-map-tests + url = https://github.com/takikawa/source-map-tests.git diff --git a/test/source-map-tests b/test/source-map-tests new file mode 160000 index 00000000..b2852261 --- /dev/null +++ b/test/source-map-tests @@ -0,0 +1 @@ +Subproject commit b2852261baf54df31ac33e2ccc5c8fe246c9a4bb diff --git a/test/test-spec-tests.js b/test/test-spec-tests.js new file mode 100644 index 00000000..48fa5dc3 --- /dev/null +++ b/test/test-spec-tests.js @@ -0,0 +1,91 @@ +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2024 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + +const fs = require('node:fs/promises'); +const SourceMapConsumer = + require("../lib/source-map-consumer").SourceMapConsumer; + +const sourceMapSpecTests = require("./source-map-tests/source-map-spec-tests.json"); + +async function readJSON(path) { + const file = await fs.open(require.resolve(path)); + const json = JSON.parse(await file.readFile()); + file.close(); + return json; +} + +// Known failures due to intentional implementation choices or due to bugs. +const skippedTests = [ + // Versions are explicitly checked a bit loosely. + "versionNumericString", + // Stricter sources array checking isn't implemented. + "sourcesNotStringOrNull", + "sourcesAndSourcesContentBothNull", + // Stricter names array checking isn't implemented. + "namesMissing", + "namesNotString", + // This check isn't as strict in this library. + "invalidMappingNotAString1", + // A mapping segment with no fields is technically invalid in the spec. + "invalidMappingSegmentWithZeroFields", + // These tests fail due to imprecision in the spec about the 32-bit limit. + "invalidMappingSegmentWithColumnExceeding32Bits", + "invalidMappingSegmentWithOriginalLineExceeding32Bits", + "invalidMappingSegmentWithOriginalColumnExceeding32Bits", + // A large VLQ that should parse, but currently does not. + "validMappingLargeVLQ", + // The library currently doesn't check the types of offset lines/columns. + "indexMapOffsetLineWrongType", + "indexMapOffsetColumnWrongType", + // The spec is not totally clear about this case. + "indexMapInvalidBaseMappings", + // The spec's definition of overlap can be refined + "indexMapInvalidOverlap", + // Not clear if this test makes sense, but spec isn't clear on behavior + "validMappingNullSources" +] + +async function testMappingAction(assert, rawSourceMap, action) { + return SourceMapConsumer.with(rawSourceMap, null, (consumer) => { + let mappedPosition = consumer.generatedPositionFor({ + source: action.originalSource, + line: action.originalLine + 1, + column: action.originalColumn + }); + + assert.equal(mappedPosition.line, action.generatedLine + 1, `generated line didn't match, expected ${action.generatedLine + 1} got ${mappedPosition.line}`); + assert.equal(mappedPosition.column, action.generatedColumn, `generated column didn't match, expected ${action.generatedColumn} got ${mappedPosition.column}`); + }); +} + +for (let testCase of sourceMapSpecTests.tests) { + if (skippedTests.includes(testCase.name)) + continue; + exports[`test from source map spec tests, name: ${testCase.name}`] = + async function (assert) { + const json = await readJSON(`./source-map-tests/resources/${testCase.sourceMapFile}`); + let sourceMapFailed = false; + try { + const map = await new SourceMapConsumer(json); + map.eachMapping(() => {}); + map.destroy(); + } catch (exn) { + if (testCase.sourceMapIsValid) + assert.fail("Expected valid source map but failed to load successfully: " + exn.message); + return; + } + if (!testCase.sourceMapIsValid) + assert.fail("Expected invalid source map but loaded successfully"); + if (testCase.testActions) { + for (let testAction of testCase.testActions) { + if (testAction.actionType == "checkMapping") { + await testMappingAction(assert, json, testAction); + } + } + } + }; +};