equicord-companion
Version:
Equicord Companion is a vscode extension to test Equicord patches & webpack finds right from the comfort of your IDE
501 lines (457 loc) • 23.2 kB
text/typescript
import { TypeAssert } from "@ast/util";
import { WebpackAstParser } from "@ast/webpack";
import { RangeExportMap } from "@type/ast";
import { expect } from "chai";
import { resolve } from "node:path";
import { Location, Position, Range, Uri } from "vscode";
describe("WebpackAstParser", function () {
const normalModule: string = require("test://ast/webpack/module.js");
it("constructs", function () {
new WebpackAstParser(normalModule);
});
it("parses the module ID", function () {
const parser = new WebpackAstParser(normalModule);
expect(parser.moduleId).to.equal("317269");
});
describe("export parsing", function () {
// TODO: add length check for wreq.d args
describe("wreq.d", function () {
it("parses a simple module", function () {
const parser = new WebpackAstParser(normalModule);
const map: RangeExportMap = parser.getExportMap();
expect(map).to.have.keys("TB", "VY", "ZP");
for (const expName in map) {
expect(map[expName]).to.have.length(2);
// both should be truthy
expect(map[expName][0] || map[expName][1], "Both are not truthy").to.be.ok;
}
// the `ZP` of
// ```js
// n.d(t, {
// ZP: () => ident
// })
// ```
expect(map.TB[0]).to.deep.equal(new Range(4, 8, 4, 10));
expect(map.VY[0]).to.deep.equal(new Range(5, 8, 5, 10));
expect(map.ZP[0]).to.deep.equal(new Range(6, 8, 6, 10));
// the identifier where its used
expect(map.TB[1]).to.deep.equal(new Range(162, 13, 162, 14));
expect(map.VY[1]).to.deep.equal(new Range(183, 13, 183, 14));
expect(map.ZP[1]).to.deep.equal(new Range(87, 13, 87, 14));
});
it("parses a module with a string literal export", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/wreq.d/simpleString.js"));
const map = parser.getExportMap();
expect(map).to.have.keys("STRING_EXPORT");
expect(map.STRING_EXPORT).to.deep.equal([new Range(5, 8, 5, 21), new Range(7, 12, 7, 31)]);
});
it("parses a module with an object literal export", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/wreq.d/objectExport.js"));
const map = parser.getExportMap();
expect(map).to.deep.equal({
EO: [
new Range(5, 8, 5, 10),
new Range(124, 13, 124, 14),
],
ZP: {
getName: [
new Range(156, 8, 156, 15),
new Range(53, 13, 53, 14),
],
useName: [
new Range(157, 8, 157, 15),
new Range(62, 13, 62, 14),
],
isNameConcealed: [
new Range(158, 8, 158, 23),
new Range(158, 25, 158, 29),
],
getUserTag: [
new Range(159, 8, 159, 18),
new Range(142, 13, 142, 14),
],
useUserTag: [
new Range(160, 8, 160, 18),
new Range(160, 20, 160, 34),
],
getFormattedName: [
new Range(164, 8, 164, 24),
new Range(81, 13, 81, 14),
],
getGlobalName: [
new Range(165, 8, 165, 21),
new Range(72, 13, 72, 14),
],
humanizeStatus: [
new Range(166, 8, 166, 22),
new Range(90, 13, 90, 14),
],
useDirectMessageRecipient: [
new Range(167, 8, 167, 33),
new Range(147, 13, 147, 14),
],
[WebpackAstParser.SYM_CJS_DEFAULT]: [new Range(155, 12, 155, 13)],
},
});
});
it("parses a module with an exported object with a computed property", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/wreq.d/computedPropInObj.js"));
const map = parser.getExportMap();
expect(map).to.deep.equal({
Z: {
"[n(231338).Et.GET_PLATFORM_BEHAVIORS]": {
handler: [
new Range(8, 12, 8, 19),
new Range(8, 21, 8, 26),
],
[WebpackAstParser.SYM_CJS_DEFAULT]: [new Range(7, 47, 7, 48)],
},
[WebpackAstParser.SYM_CJS_DEFAULT]: [new Range(6, 12, 6, 13)],
},
});
});
});
describe("module.exports", function () {
it("parses a module with an object literal export (class names)", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/e.exports/objLiteral.js"));
const map = parser.getExportMap();
expect(map).to.deep.equal({
productListingsHeader: [
new Range(5, 8, 5, 29),
new Range(5, 31, 5, 61),
],
productListings: [
new Range(6, 8, 6, 23),
new Range(6, 25, 6, 49),
],
addButton: [
new Range(7, 8, 7, 17),
new Range(7, 19, 7, 37),
],
addButtonInner: [
new Range(8, 8, 8, 22),
new Range(8, 24, 8, 47),
],
[WebpackAstParser.SYM_CJS_DEFAULT]: [new Range(4, 16, 4, 17)],
});
});
it("parses a single string export", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/e.exports/string.js"));
const map = parser.getExportMap();
expect(map).to.deep.equal({
[WebpackAstParser.SYM_CJS_DEFAULT]: [new Range(4, 16, 4, 46)],
});
});
it("parses a re-export", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/e.exports/identReExport.js"));
const map = parser.getExportMap();
expect(map).to.have.keys(WebpackAstParser.SYM_CJS_DEFAULT);
expect(map[WebpackAstParser.SYM_CJS_DEFAULT]).to.have.length(1);
expect(map[WebpackAstParser.SYM_CJS_DEFAULT][0]).to.deep.equal(new Range(4, 12, 4, 21));
});
it("parses exports in an intermediate variable", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/e.exports/ident.js"));
const map = parser.getExportMap();
expect(map).to.deep.equal({
headerContainer: [
new Range(5, 8, 5, 23),
new Range(5, 25, 5, 49),
],
closeContainer: [
new Range(6, 8, 6, 22),
new Range(6, 24, 6, 47),
],
closeIcon: [
new Range(7, 8, 7, 17),
new Range(7, 19, 7, 37),
],
headerImage: [
new Range(8, 8, 8, 19),
new Range(8, 21, 8, 41),
],
headerImageContainer: [
new Range(9, 8, 9, 28),
new Range(9, 30, 9, 59),
],
confirmationContainer: [
new Range(10, 8, 10, 29),
new Range(10, 31, 10, 61),
],
purchaseConfirmation: [
new Range(11, 8, 11, 28),
new Range(11, 30, 11, 88),
],
confirmationTitle: [
new Range(12, 8, 12, 25),
new Range(12, 27, 12, 53),
],
confirmationSubtitle: [
new Range(13, 8, 13, 28),
new Range(13, 30, 13, 59),
],
[WebpackAstParser.SYM_CJS_DEFAULT]: [new Range(4, 12, 4, 13)],
});
});
it("parses a function expression", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/e.exports/function.js"));
const map = parser.getExportMap();
expect(map).to.have.keys(WebpackAstParser.SYM_CJS_DEFAULT);
expect(map[WebpackAstParser.SYM_CJS_DEFAULT]).to.have.length(1);
expect(map[WebpackAstParser.SYM_CJS_DEFAULT][0]).to.deep.equal(new Range(9, 16, 9, 27));
});
it("parses everything else", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/e.exports/everythingElse.js"));
const map = parser.getExportMap();
expect(map).to.have.keys(WebpackAstParser.SYM_CJS_DEFAULT);
expect(map[WebpackAstParser.SYM_CJS_DEFAULT]).to.have.length(1);
expect(map[WebpackAstParser.SYM_CJS_DEFAULT][0]).to.deep.equal(new Range(5, 16, 5, 44));
});
});
describe("exports", function () {
it("Parses exports properly", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/exports/module.js"));
const keys = [
"Deflate",
"deflate",
"deflateRaw",
"gzip",
];
const map = parser.getExportMap();
expect(map).to.have.keys(keys);
keys.forEach((key) => {
expect(map[key]).to.have.length(3);
});
keys.forEach((key, i) => {
expect(map[key][0]).to.deep.equal(new Range(101 + i, 6, 101 + i, 6 + key.length));
expect(map[key][1]).to.deep.equal(new Range(101 + i, 9 + key.length, 101 + i, 10 + key.length));
});
expect(map.Deflate[2]).to.deep.equal(new Range(18, 13, 18, 14));
expect(map.deflate[2]).to.deep.equal(new Range(49, 13, 49, 14));
expect(map.deflateRaw[2]).to.deep.equal(new Range(56, 13, 56, 14));
expect(map.gzip[2]).to.deep.equal(new Range(60, 13, 60, 14));
});
});
describe("stores", function () {
it("generates the proper export map for a store exported with wreq.d", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/stores/store1.js"));
const map = parser.getExportMap();
expect(map).to.have.keys("Z");
expect(map.Z).to.have.keys(["initialize", "isVisible", WebpackAstParser.SYM_CJS_DEFAULT]);
TypeAssert<RangeExportMap>(map.Z);
expect(map.Z[WebpackAstParser.SYM_CJS_DEFAULT]).to.deep.equal([
new Range(4, 8, 4, 9),
new Range(32, 16, 32, 17),
new Range(10, 10, 10, 11),
]);
expect(map.Z.initialize).to.deep.equal([new Range(11, 8, 11, 18)]);
expect(map.Z.isVisible).to.deep.equal([new Range(18, 8, 18, 17)]);
});
it("generates the proper export map for a store constructed with no arguments", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/stores/store2.js"));
const map = parser.getExportMap();
expect(map).to.have.keys("default", "mergeUser", "ASSISTANT_WUMPUS_VOICE_USER");
expect(map.default).to.deep.equal({
initialize: [new Range(212, 8, 212, 18)],
takeSnapshot: [new Range(215, 8, 215, 20)],
handleLoadCache: [new Range(224, 8, 224, 23)],
getUserStoreVersion: [new Range(38, 12, 38, 13)],
getUser: [new Range(241, 8, 241, 15)],
getUsers: [new Range(245, 8, 245, 16)],
forEach: [new Range(248, 8, 248, 15)],
findByTag: [new Range(253, 8, 253, 17)],
filter: [new Range(260, 8, 260, 14)],
getCurrentUser: [new Range(270, 8, 270, 22)],
[WebpackAstParser.SYM_CJS_DEFAULT]: [
new Range(7, 8, 7, 15),
new Range(286, 17, 286, 19),
new Range(211, 10, 211, 12),
new Range(273, 8, 282, 9),
],
});
expect(map.ASSISTANT_WUMPUS_VOICE_USER).to.deep.equal([
new Range(6, 8, 6, 35),
new Range(39, 12, 39, 31),
]);
expect(map.mergeUser).to.deep.equal([
new Range(8, 8, 8, 17),
new Range(118, 13, 118, 14),
]);
});
it("generates the proper export map for a store with no initialize method", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/stores/store3.js"));
const map = parser.getExportMap();
expect(map).to.have.keys("Z");
expect(map.Z).to.deep.equal({
getGuild: [new Range(199, 8, 199, 16)],
getGuilds: [new Range(203, 8, 203, 17)],
getGuildIds: [new Range(206, 8, 206, 19)],
// getGuildCount: [new Range(209, 8, 209, 21)],
getGuildCount: [new Range(4, 8, 4, 9)],
// isLoaded: [new Range(212, 8, 212, 16)],
isLoaded: [new Range(52, 12, 52, 14)],
getGeoRestrictedGuilds: [new Range(53, 12, 53, 14)],
getAllGuildsRoles: [new Range(218, 8, 218, 25)],
getRoles: [new Range(221, 8, 221, 16)],
getRole: [new Range(225, 8, 225, 15)],
[WebpackAstParser.SYM_CJS_DEFAULT]: [
new Range(6, 8, 6, 9),
new Range(231, 16, 231, 17),
new Range(198, 10, 198, 11),
],
});
});
it("generates the proper export map for a store with getters", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/stores/getter.js"));
const map = parser.getExportMap();
// Change when parsing is fixed to only return to constants
expect(map).to.deep.equal({
Z: {
[WebpackAstParser.SYM_CJS_DEFAULT]: [
new Range(4, 8, 4, 9),
new Range(24, 16, 24, 17),
new Range(9, 10, 9, 11),
],
keepOpen: [new Range(8, 12, 8, 14)],
enabled: [new Range(7, 12, 7, 14)],
},
});
});
it.skip("generates the proper export map for a store exported with wreq.t", function () {
// I've never seen a store exported with wreq.t
});
it.skip("generates the proper export map for a store exported with wreq.e", function () {
// I've never seen a store exported with wreq.e
});
});
});
describe("import parsing", function () {
it("parses an only re-exported export properly", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/reExport.js"));
const test = parser.getUsesOfImport("999001", "foo");
expect(test).to.deep.equal([new Range(5, 21, 5, 24)]);
});
it("parses a re-export with other uses", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/reExport.js"));
const test = parser.getUsesOfImport("999001", "bar");
expect(test).to.have.deep.members([new Range(6, 22, 6, 25), new Range(10, 18, 10, 21)]);
});
it("returns [] when there are no uses of that export for that module", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/reExport.js"));
const test = parser.getUsesOfImport("999001", "baz");
expect(test).to.deep.equal([]);
});
it("returns [] when the module ID is not imported", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/reExport.js"));
const text = parser.getUsesOfImport("999003", "foo");
expect(text).to.deep.equal([]);
});
it("returns [] when there are no uses of that export for that module 2", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/indirectCall.js"));
const test = parser.getUsesOfImport("999002", "bar");
expect(test).to.deep.equal([]);
});
it("returns [] when the module ID is not imported 2", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/indirectCall.js"));
const text = parser.getUsesOfImport("999004", "foo");
expect(text).to.deep.equal([]);
});
it("parses an indirect call properly", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/indirectCall.js"));
const test = parser.getUsesOfImport("999002", "foo");
expect(test).to.deep.equal([new Range(9, 22, 9, 25)]);
});
it("throws when wreq is not used", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/noWreq.js"));
// args should not matter as it should throw before
expect(parser.getUsesOfImport.bind(parser)).to.throw("Wreq is not used in this file");
});
it("parses node default exports correctly", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/nodeModule.js"));
const test = parser.getUsesOfImport("999005", WebpackAstParser.SYM_CJS_DEFAULT);
expect(test).to.have.deep.members([new Range(20, 15, 20, 19), new Range(15, 8, 15, 12)]);
});
it("parsed named node exports correctly", function () {
const parser = new WebpackAstParser(require("test://ast/webpack/imports/nodeModule.js"));
const test = parser.getUsesOfImport("999005", "qux");
expect(test).to.have.deep.members([new Range(19, 13, 19, 16), new Range(16, 20, 16, 23)]);
});
});
// INFO: this assumes that the cache is working properly
// TODO: add septate testing for the cache
describe("cache parsing", function () {
function makeModulePath(file: string): Uri {
return Uri.file(resolve(
__dirname,
"..",
"..",
"..",
"assets",
"test",
"ast",
".modules",
file,
));
}
function makeLineRange(file: string | number, y1, x1: number, len = 1) {
if (typeof file === "number") {
file = `${file * 111111}.js`;
}
return new Location(
makeModulePath(file),
new Range(y1, x1, y1, x1 + len),
);
}
describe("re-export handling", function () {
it("handles re-exports across wreq.d", async function () {
const parser = new WebpackAstParser(require("test://ast/.modules/333333.js"));
const locs = await parser.generateReferences(new Position(5, 8));
expect(locs).to.have.deep.members([
makeLineRange(2, 18, 29),
makeLineRange(1, 23, 34),
makeLineRange(4, 5, 20),
makeLineRange(5, 12, 30),
makeLineRange(5, 17, 30),
]);
});
it.skip("handles re-exports across wreq.t", async function () {
});
it.skip("handles re-exports across wreq.e", async function () {
});
});
it("finds a simple use in only one file", async function () {
const parser = new WebpackAstParser(require("test://ast/.modules/222222.js"));
const locs = await parser.generateReferences(new Position(6, 8));
expect(locs).to.deep.equal([makeLineRange(1, 15, 26)]);
});
it("finds a simple export in more than one file", async function () {
const parser = new WebpackAstParser(require("test://ast/.modules/222222.js"));
const locs = await parser.generateReferences(new Position(5, 8));
expect(locs).to.have.deep.members([
makeLineRange(1, 15, 18),
makeLineRange(1, 15, 40),
makeLineRange(9, 13, 41),
]);
});
it.skip("finds all uses of a default e.exports", function () {
});
/**
* ```js
* function foo() {
* }
* function bar() {
* }
* foo.bar = bar;
* e.exports = foo;
* ```
*/
it.skip("finds all uses of a default e.exports where the exports are assigned to the default export first", function () {
});
describe("stores", function () {
it("finds all uses of a store from the class name", async function () {
const parser = new WebpackAstParser(require("test://ast/.modules/999999.js"));
const locs = await parser.generateReferences(new Position(8, 11));
console.log(locs);
});
});
});
});