text: /* 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(
  			request,
  			userRequest,
  			rawRequest,
  			loaders,
  			resource,
  			parser
  		);
  	});
  	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(
  					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(
  					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(
  					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.fileDependencies = fileDependencies;
  			normalModule.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);
  						});
  					});
  				});
  			});
  		});
  	});
  });
  