text: /* globals describe, it */
  "use strict";
  
  const path = require("path");
  const fs = require("fs");
  const rimraf = require("rimraf");
  
  const webpack = require("../");
  const WebpackOptionsDefaulter = require("../lib/WebpackOptionsDefaulter");
  let fixtureCount = 0;
  
  describe("Compiler (caching)", () => {
  	jest.setTimeout(15000);
  
  	function compile(entry, options, callback) {
  		options.mode = "none";
  		options = new WebpackOptionsDefaulter().process(options);
  		options.cache = true;
  		options.entry = entry;
  		options.optimization.minimize = false;
  		options.context = path.join(__dirname, "fixtures");
  		options.output.path = "/";
  		options.output.filename = "bundle.js";
  		options.output.pathinfo = true;
  		const logs = {
  			mkdirp: [],
  			writeFile: []
  		};
  
  		const c = webpack(options);
  		const files = {};
  		c.outputFileSystem = {
  			join() {
  				return [].join.call(arguments, "/").replace(/\/+/g, "/");
  			},
  			mkdirp(path, callback) {
  				logs.mkdirp.push(path);
  				callback();
  			},
  			writeFile(name, content, callback) {
  				logs.writeFile.push(name, content);
  				files[name] = content.toString("utf-8");
  				callback();
  			}
  		};
  		c.hooks.compilation.tap(
  			"CompilerCachingTest",
  			compilation => (compilation.bail = true)
  		);
  
  		let compilerIteration = 1;
  
  		function runCompiler(options, callback) {
  			if (typeof options === "function") {
  				callback = options;
  				options = {};
  			}
  			c.run((err, stats) => {
  				if (err) throw err;
  				expect(typeof stats).toBe("object");
  				stats = stats.toJson({
  					modules: true,
  					reasons: true
  				});
  				expect(typeof stats).toBe("object");
  				expect(stats).toHaveProperty("errors");
  				expect(Array.isArray(stats.errors)).toBe(true);
  				if (options.expectErrors) {
  					expect(stats.errors).toHaveLength(options.expectErrors);
  				} else {
  					if (stats.errors.length > 0) {
  						expect(typeof stats.errors[0]).toBe("string");
  						throw new Error(stats.errors[0]);
  					}
  				}
  				stats.logs = logs;
  				callback(stats, files, compilerIteration++);
  			});
  		}
  
  		runCompiler(callback);
  
  		return {
  			compilerInstance: c,
  			runAgain: runCompiler
  		};
  	}
  
  	const tempFixturePath = path.join(
  		__dirname,
  		"fixtures",
  		"temp-cache-fixture"
  	);
  
  	function cleanup() {
  		rimraf.sync(`${tempFixturePath}-*`);
  	}
  
  	beforeAll(cleanup);
  	afterAll(cleanup);
  
  	function createTempFixture() {
  		const fixturePath = `${tempFixturePath}-${fixtureCount}`;
  		const aFilepath = path.join(fixturePath, "a.js");
  		const cFilepath = path.join(fixturePath, "c.js");
  
  		// Remove previous copy if present
  		rimraf.sync(fixturePath);
  
  		// Copy over file since we"ll be modifying some of them
  		fs.mkdirSync(fixturePath);
  		fs.createReadStream(path.join(__dirname, "fixtures", "a.js")).pipe(
  			fs.createWriteStream(aFilepath)
  		);
  		fs.createReadStream(path.join(__dirname, "fixtures", "c.js")).pipe(
  			fs.createWriteStream(cFilepath)
  		);
  
  		fixtureCount++;
  		return {
  			rootPath: fixturePath,
  			aFilepath: aFilepath,
  			cFilepath: cFilepath
  		};
  	}
  
  	it("should cache single file (with manual 1s wait) ", done => {
  		const options = {};
  		const tempFixture = createTempFixture();
  
  		const helper = compile(tempFixture.cFilepath, options, (stats, files) => {
  			// Not cached the first time
  			expect(stats.assets[0].name).toBe("bundle.js");
  			expect(stats.assets[0].emitted).toBe(true);
  
  			helper.runAgain((stats, files, iteration) => {
  				// Cached the second run
  				expect(stats.assets[0].name).toBe("bundle.js");
  				expect(stats.assets[0].emitted).toBe(false);
  
  				const aContent = fs
  					.readFileSync(tempFixture.aFilepath)
  					.toString()
  					.replace("This is a", "This is a MODIFIED");
  
  				setTimeout(() => {
  					fs.writeFileSync(tempFixture.aFilepath, aContent);
  
  					helper.runAgain((stats, files, iteration) => {
  						// Cached the third run
  						expect(stats.assets[0].name).toBe("bundle.js");
  						expect(stats.assets[0].emitted).toBe(true);
  
  						done();
  					});
  				}, 1100);
  			});
  		});
  	});
  
  	it("should cache single file (even with no timeout) ", done => {
  		const options = {};
  		const tempFixture = createTempFixture();
  
  		const helper = compile(tempFixture.cFilepath, options, (stats, files) => {
  			// Not cached the first time
  			expect(stats.assets[0].name).toBe("bundle.js");
  			expect(stats.assets[0].emitted).toBe(true);
  
  			helper.runAgain((stats, files, iteration) => {
  				// Cached the second run
  				expect(stats.assets[0].name).toBe("bundle.js");
  				expect(stats.assets[0].emitted).toBe(false);
  
  				expect(files["/bundle.js"]).toMatch("This is a");
  
  				const aContent = fs
  					.readFileSync(tempFixture.aFilepath)
  					.toString()
  					.replace("This is a", "This is a MODIFIED");
  
  				fs.writeFileSync(tempFixture.aFilepath, aContent);
  
  				helper.runAgain((stats, files, iteration) => {
  					// Cached the third run
  					expect(stats.assets[0].name).toBe("bundle.js");
  					expect(stats.assets[0].emitted).toBe(true);
  
  					expect(files["/bundle.js"]).toMatch("This is a MODIFIED");
  
  					done();
  				});
  			});
  		});
  	});
  
  	it("should only build when modified (with manual 2s wait)", done => {
  		const options = {};
  		const tempFixture = createTempFixture();
  
  		const helper = compile(tempFixture.cFilepath, options, (stats, files) => {
  			// Built the first time
  			expect(stats.modules[0].name).toMatch("c.js");
  			expect(stats.modules[0].built).toBe(true);
  
  			expect(stats.modules[1].name).toMatch("a.js");
  			expect(stats.modules[1].built).toBe(true);
  
  			setTimeout(() => {
  				helper.runAgain((stats, files, iteration) => {
  					// Not built when cached the second run
  					expect(stats.modules[0].name).toMatch("c.js");
  					// expect(stats.modules[0].built).toBe(false);
  
  					expect(stats.modules[1].name).toMatch("a.js");
  					// expect(stats.modules[1].built).toBe(false);
  
  					const aContent = fs
  						.readFileSync(tempFixture.aFilepath)
  						.toString()
  						.replace("This is a", "This is a MODIFIED");
  
  					setTimeout(() => {
  						fs.writeFileSync(tempFixture.aFilepath, aContent);
  
  						helper.runAgain((stats, files, iteration) => {
  							// And only a.js built after it was modified
  							expect(stats.modules[0].name).toMatch("c.js");
  							expect(stats.modules[0].built).toBe(false);
  
  							expect(stats.modules[1].name).toMatch("a.js");
  							expect(stats.modules[1].built).toBe(true);
  
  							done();
  						});
  					}, 2100);
  				});
  			}, 4100);
  		});
  	});
  
  	it("should build when modified (even with no timeout)", done => {
  		const options = {};
  		const tempFixture = createTempFixture();
  
  		const helper = compile(tempFixture.cFilepath, options, (stats, files) => {
  			// Built the first time
  			expect(stats.modules[0].name).toMatch("c.js");
  			expect(stats.modules[0].built).toBe(true);
  
  			expect(stats.modules[1].name).toMatch("a.js");
  			expect(stats.modules[1].built).toBe(true);
  
  			helper.runAgain((stats, files, iteration) => {
  				// Not built when cached the second run
  				expect(stats.modules[0].name).toMatch("c.js");
  				// expect(stats.modules[0].built).toBe(false);
  
  				expect(stats.modules[1].name).toMatch("a.js");
  				// expect(stats.modules[1].built).toBe(false);
  
  				const aContent = fs
  					.readFileSync(tempFixture.aFilepath)
  					.toString()
  					.replace("This is a", "This is a MODIFIED");
  
  				fs.writeFileSync(tempFixture.aFilepath, aContent);
  
  				helper.runAgain((stats, files, iteration) => {
  					// And only a.js built after it was modified
  					expect(stats.modules[0].name).toMatch("c.js");
  					// expect(stats.modules[0].built).toBe(false);
  
  					expect(stats.modules[1].name).toMatch("a.js");
  					expect(stats.modules[1].built).toBe(true);
  
  					done();
  				});
  			});
  		});
  	});
  });
  