/* globals describe, it, beforeEach, afterEach */ "use strict"; require("should"); const sinon = require("sinon"); const NormalModule = require("../lib/NormalModule"); const NullDependency = require("../lib/dependencies/NullDependency"); const SourceMapSource = require("webpack-sources").SourceMapSource; const OriginalSource = require("webpack-sources").OriginalSource; const RawSource = require("webpack-sources").RawSource; describe("NormalModule", function() { let normalModule; let request; let userRequest; let rawRequest; let loaders; let resource; let parser; beforeEach(function() { request = "some/request"; userRequest = "some/userRequest"; rawRequest = "some/rawRequest"; loaders = []; resource = "some/resource"; parser = { parse() {} }; normalModule = new NormalModule( "javascript/auto", request, userRequest, rawRequest, loaders, resource, parser ); normalModule.buildInfo = { cacheable: true }; }); describe("#identifier", function() { it("returns an identifier for this module", function() { normalModule.identifier().should.eql(request); }); it("returns an identifier from toString", function() { normalModule.debugId = 1000; normalModule.toString().should.eql("Module[1000]"); normalModule.id = 1; normalModule.toString().should.eql("Module[1]"); }); }); describe("#readableIdentifier", function() { it("calls the given requestShortener with the user request", function() { const spy = sinon.spy(); normalModule.readableIdentifier({ shorten: spy }); spy.callCount.should.eql(1); spy.args[0][0].should.eql(userRequest); }); }); describe("#libIdent", function() { it("contextifies the userRequest of the module", function() { normalModule.libIdent({ context: "some/context" }).should.eql("../userRequest"); }); describe("given a userRequest containing loaders", function() { beforeEach(function() { userRequest = "some/userRequest!some/other/userRequest!some/thing/is/off/here"; normalModule = new NormalModule( "javascript/auto", request, userRequest, rawRequest, loaders, resource, parser ); }); it("contextifies every path in the userRequest", function() { normalModule.libIdent({ context: "some/context" }).should.eql("../userRequest!../other/userRequest!../thing/is/off/here"); }); }); describe("given a userRequest containing query parameters", function() { it("ignores paths in query parameters", function() { userRequest = "some/context/loader?query=foo\\bar&otherPath=testpath/other"; normalModule = new NormalModule( "javascript/auto", request, userRequest, rawRequest, loaders, resource, parser ); normalModule.libIdent({ context: "some/context", }).should.eql("./loader?query=foo\\bar&otherPath=testpath/other"); }); }); }); describe("#nameForCondition", function() { it("return the resource", function() { normalModule.nameForCondition().should.eql(resource); }); describe("given a resource containing a ?-sign", function() { const baseResource = "some/resource"; beforeEach(function() { resource = baseResource + "?some=query"; normalModule = new NormalModule( "javascript/auto", request, userRequest, rawRequest, loaders, resource, parser ); }); it("return only the part before the ?-sign", function() { normalModule.nameForCondition().should.eql(baseResource); }); }); }); describe("#createSourceForAsset", function() { let name; let content; let sourceMap; beforeEach(function() { name = "some name"; content = "some content"; sourceMap = "some sourcemap"; }); describe("given no sourcemap", function() { it("returns a RawSource", function() { normalModule.createSourceForAsset(name, content).should.be.instanceOf(RawSource); }); }); describe("given a string as the sourcemap", function() { it("returns a OriginalSource", function() { normalModule.createSourceForAsset(name, content, sourceMap).should.be.instanceOf(OriginalSource); }); }); describe("given a some other kind of sourcemap", function() { beforeEach(function() { sourceMap = () => {}; }); it("returns a SourceMapSource", function() { normalModule.createSourceForAsset(name, content, sourceMap).should.be.instanceOf(SourceMapSource); }); }); }); describe("#source", function() { describe("without the module having any source", function() { beforeEach(function() { normalModule._source = null; }); it("returns a Source containing an Error", function() { normalModule.source().should.be.instanceOf(RawSource); normalModule.source().source().should.eql("throw new Error('No source available');"); }); }); }); describe("#originalSource", function() { let expectedSource = "some source"; beforeEach(function() { normalModule._source = new RawSource(expectedSource); }); it("returns an original Source", function() { normalModule.originalSource().should.eql(normalModule._source); }); }); describe("#updateHashWithSource", function() { let hashSpy; let hash; beforeEach(function() { hashSpy = sinon.spy(); hash = { update: hashSpy }; }); describe("without the module having any source", function() { beforeEach(function() { normalModule._source = null; }); it("calls hash function with \"null\"", function() { normalModule.updateHashWithSource(hash); hashSpy.callCount.should.eql(1); hashSpy.args[0][0].should.eql("null"); }); }); describe("without the module having source", function() { let expectedSource = "some source"; beforeEach(function() { normalModule._source = new RawSource(expectedSource); }); it("calls hash function with \"source\" and then the actual source of the module", function() { normalModule.updateHashWithSource(hash); hashSpy.callCount.should.eql(2); hashSpy.args[0][0].should.eql("source"); hashSpy.args[1][0].should.eql(expectedSource); }); }); }); describe("#hasDependencies", function() { it("returns true if has dependencies", function() { normalModule.addDependency(new NullDependency()); normalModule.hasDependencies().should.eql(true); }); it("returns false if has dependencies", function() { normalModule.hasDependencies().should.eql(false); }); }); describe("#needRebuild", function() { let fileTimestamps; let contextTimestamps; let fileDependencies; let contextDependencies; let fileA; let fileB; function setDeps( fileDependencies, contextDependencies) { normalModule.buildInfo.fileDependencies = fileDependencies; normalModule.buildInfo.contextDependencies = contextDependencies; } beforeEach(function() { fileA = "fileA"; fileB = "fileB"; fileDependencies = [fileA, fileB]; contextDependencies = [fileA, fileB]; fileTimestamps = { [fileA]: 1, [fileB]: 1, }; contextTimestamps = { [fileA]: 1, [fileB]: 1, }; normalModule.buildTimestamp = 2; setDeps(fileDependencies, contextDependencies); }); describe("given all timestamps are older than the buildTimestamp", function() { it("returns false", function() { normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(false); }); }); describe("given a file timestamp is newer than the buildTimestamp", function() { beforeEach(function() { fileTimestamps[fileA] = 3; }); it("returns true", function() { normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true); }); }); describe("given a no file timestamp exists", function() { beforeEach(function() { fileTimestamps = {}; }); it("returns true", function() { normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true); }); }); describe("given a context timestamp is newer than the buildTimestamp", function() { beforeEach(function() { contextTimestamps[fileA] = 3; }); it("returns true", function() { normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true); }); }); describe("given a no context timestamp exists", function() { beforeEach(function() { contextTimestamps = {}; }); it("returns true", function() { normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true); }); }); }); describe("#splitVariablesInUniqueNamedChunks", function() { let variables; beforeEach(function() { variables = [{ name: "foo" }, { name: "bar" }, { name: "baz" }, { name: "some" }, { name: "more" }]; }); describe("given an empty array of vars", function() { it("returns an empty array", function() { normalModule.splitVariablesInUniqueNamedChunks([]).should.eql([ [] ]); }); }); describe("given an array of distrinct variables", function() { it("returns an array containing an array containing the variables", function() { normalModule.splitVariablesInUniqueNamedChunks(variables).should.eql([variables]); }); }); describe("given an array with duplicate variables", function() { it("returns several arrays each containing only distinct variable names", function() { normalModule.splitVariablesInUniqueNamedChunks(variables.concat(variables)).should.eql([variables, variables]); }); describe("and a duplicate as the last variable", function() { it("returns correctly split distinct arrays", function() { normalModule.splitVariablesInUniqueNamedChunks(variables.concat(variables).concat(variables[0])).should.eql([variables, variables, [variables[0]]]); }); }); }); }); describe("#applyNoParseRule", function() { let rule; let content; describe("given a string as rule", function() { beforeEach(function() { rule = "some-rule"; }); describe("and the content starting with the string specified in rule", function() { beforeEach(function() { content = rule + "some-content"; }); it("returns true", function() { normalModule.shouldPreventParsing(rule, content).should.eql(true); }); }); describe("and the content does not start with the string specified in rule", function() { beforeEach(function() { content = "some-content"; }); it("returns false", function() { normalModule.shouldPreventParsing(rule, content).should.eql(false); }); }); }); describe("given a regex as rule", function() { beforeEach(function() { rule = /some-rule/; }); describe("and the content matches the rule", function() { beforeEach(function() { content = rule + "some-content"; }); it("returns true", function() { normalModule.shouldPreventParsing(rule, content).should.eql(true); }); }); describe("and the content does not match the rule", function() { beforeEach(function() { content = "some-content"; }); it("returns false", function() { normalModule.shouldPreventParsing(rule, content).should.eql(false); }); }); }); }); describe("#shouldPreventParsing", function() { let applyNoParseRuleSpy; beforeEach(function() { applyNoParseRuleSpy = sinon.stub(); normalModule.applyNoParseRule = applyNoParseRuleSpy; }); describe("given no noParseRule", function() { it("returns false", function() { normalModule.shouldPreventParsing().should.eql(false); applyNoParseRuleSpy.callCount.should.eql(0); }); }); describe("given a noParseRule", function() { let returnValOfSpy; beforeEach(function() { returnValOfSpy = true; applyNoParseRuleSpy.returns(returnValOfSpy); }); describe("that is a string", function() { it("calls and returns whatever applyNoParseRule returns", function() { normalModule.shouldPreventParsing("some rule").should.eql(returnValOfSpy); applyNoParseRuleSpy.callCount.should.eql(1); }); }); describe("that is a regex", function() { it("calls and returns whatever applyNoParseRule returns", function() { normalModule.shouldPreventParsing("some rule").should.eql(returnValOfSpy); applyNoParseRuleSpy.callCount.should.eql(1); }); }); describe("that is an array", function() { describe("of strings and or regexs", function() { let someRules; beforeEach(function() { someRules = [ "some rule", /some rule1/, "some rule2", ]; }); describe("and none of them match", function() { beforeEach(function() { returnValOfSpy = false; applyNoParseRuleSpy.returns(returnValOfSpy); }); it("returns false", function() { normalModule.shouldPreventParsing(someRules).should.eql(returnValOfSpy); applyNoParseRuleSpy.callCount.should.eql(3); }); }); describe("and the first of them matches", function() { beforeEach(function() { returnValOfSpy = true; applyNoParseRuleSpy.returns(returnValOfSpy); }); it("returns true", function() { normalModule.shouldPreventParsing(someRules).should.eql(returnValOfSpy); applyNoParseRuleSpy.callCount.should.eql(1); }); }); describe("and the last of them matches", function() { beforeEach(function() { returnValOfSpy = true; applyNoParseRuleSpy.onCall(0).returns(false); applyNoParseRuleSpy.onCall(1).returns(false); applyNoParseRuleSpy.onCall(2).returns(true); }); it("returns true", function() { normalModule.shouldPreventParsing(someRules).should.eql(returnValOfSpy); applyNoParseRuleSpy.callCount.should.eql(3); }); }); }); }); }); }); });