Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to inspectpack@4. Add regression tests to catch imports of format* utils. #263

Merged
merged 1 commit into from
Nov 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/webpack-dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

const commander = require("commander");
const spawn = require("cross-spawn");
const Dashboard = require("../dashboard/index.js");
const Dashboard = require("../dashboard/index");
const SocketIO = require("socket.io");

const DEFAULT_PORT = 9838;
Expand Down
8 changes: 4 additions & 4 deletions dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
const chalk = require("chalk");
const blessed = require("blessed");

const formatOutput = require("../utils/format-output.js");
const formatModules = require("../utils/format-modules.js");
const formatAssets = require("../utils/format-assets.js");
const formatProblems = require("../utils/format-problems.js");
const { formatOutput } = require("../utils/format-output");
const { formatModules } = require("../utils/format-modules");
const { formatAssets } = require("../utils/format-assets");
const { formatProblems } = require("../utils/format-problems");
const { deserializeError } = require("../utils/error-serialization");

const PERCENT_MULTIPLIER = 100;
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";

const dashboard = require("./dashboard/index.js");
const dashboard = require("./dashboard/index");

module.exports = dashboard;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"cross-spawn": "^6.0.5",
"filesize": "^3.6.1",
"handlebars": "^4.0.11",
"inspectpack": "^3.0.1",
"inspectpack": "^4.0.0",
"most": "^1.7.3",
"socket.io": "^2.1.1",
"socket.io-client": "^2.1.1"
Expand Down
2 changes: 1 addition & 1 deletion plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class DashboardPlugin {
},
{
type: "progress",
value: 0
value: 1
},
{
type: "operations",
Expand Down
32 changes: 29 additions & 3 deletions test/base.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,35 @@ beforeEach(() => {

// Some manual hacking.
blessed.screen.returns({
append: () => {},
key: () => {},
render: () => {}
append: sinon.spy(),
key: sinon.spy(),
render: sinon.spy()
});

blessed.listbar.returns({
selected: "selected",
setLabel: sinon.spy(),
setProblems: sinon.spy(),
selectTab: sinon.spy(),
setItems: sinon.spy()
});

blessed.box.returns({
setContent: sinon.spy(),
setLabel: sinon.spy()
});

blessed.log.returns({
log: sinon.spy()
});

blessed.table.returns({
setData: sinon.spy()
});

blessed.ProgressBar.returns({
setContent: sinon.spy(),
setProgress: sinon.spy()
});
});

Expand Down
287 changes: 286 additions & 1 deletion test/dashboard/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
"use strict";

require("../base.spec");
const chalk = require("chalk");
const blessed = require("blessed");

const base = require("../base.spec");
const Dashboard = require("../../dashboard");

const mockSetItems = () => {
// Override ListBar fakes from what we do in `base.spec.js`.
// Note that these are **already** stubbed. We're not monkey-patching blessed.
blessed.listbar.returns({
selected: "selected",
setLabel: base.sandbox.spy(),
selectTab: base.sandbox.spy(),
setItems: base.sandbox.stub().callsFake(obj => {
// Naively simulate what setItems would do calling each object key.
Object.keys(obj).forEach(key => obj[key]());
})
});
};

describe("dashboard", () => {
const options = {
color: "red",
Expand All @@ -25,4 +41,273 @@ describe("dashboard", () => {
expect(dashboardWithOptions.color).to.equal("red");
expect(dashboardWithOptions.minimal).to.be.true;
});

describe("set* methods", () => {
let dashboard;

beforeEach(() => {
dashboard = new Dashboard();
});

describe("setData", () => {
const dataArray = [{
type: "progress",
value: 0.57
}, {
type: "operations",
value: "IDLE"
}];

it("can setData", () => {
expect(() => dashboard.setData(dataArray)).to.not.throw;
});
});

describe("setOperations", () => {
const data = {
value: "IDLE"
};

it("can setOperations", () => {
expect(() => dashboard.setOperations(data)).to.not.throw;

dashboard.setOperations(data);
expect(dashboard.operations.setContent).to.have.been.calledWith(data.value);
});
});

describe("setStatus", () => {
const data = {
value: "Success"
};

it("can setStatus", () => {
expect(() => dashboard.setStatus(data)).to.not.throw;

dashboard.setStatus(data);
expect(dashboard.status.setContent)
.to.have.been.calledWith(`{green-fg}{bold}${data.value}{/}`);
});

it("should display a failed status on build failure", () => {
data.value = "Failed";
expect(() => dashboard.setStatus(data)).to.not.throw;

dashboard.setStatus(data);
expect(dashboard.status.setContent)
.to.have.been.calledWith(`{red-fg}{bold}${data.value}{/}`);
});

it("should display any other status string without coloring", () => {
data.value = "Unknown";
expect(() => dashboard.setStatus(data)).to.not.throw;

dashboard.setStatus(data);
expect(dashboard.status.setContent)
.to.have.been.calledWith(`{bold}${data.value}{/}`);
});
});

describe("setProgress", () => {
const data = {
value: 0.57
};

it("can setProgress", () => {
expect(() => dashboard.setProgress(data)).to.not.throw;

dashboard.setProgress(data);
expect(dashboard.progressbar.setProgress).to.have.been.calledOnce;
Copy link
Contributor Author

@parkerziegler parkerziegler Nov 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make these asserts more firm, i.e. to.have.been.calledWith, but I'm hitting the good ol' JS-floating-point-math-is-horrible thing. The crux of the issue is that a float like 0.57 gets evaluated in our calculations in this method as 0.56999999997 (or something of the like), and our rounding setup returns 0.56 / 56%. Not sure if we want to update our rounding logic in this method, but wanted to call out that this is happening. I don't like the idea of writing:

const data = {
   value: 0.57
};

expect(dashboard.progressbar.setProgress).to.have.been.calledWith(0.56);

expect(dashboard.progressbar.setContent).to.have.been.called;
});

it(`should call progressbar.setProgress twice if not in minimal mode
and percent is falsy`, () => {
data.value = null;
expect(() => dashboard.setProgress(data)).to.not.throw;

dashboard.setProgress(data);
expect(dashboard.progressbar.setProgress).to.have.been.calledTwice;
});
});

describe("setStats", () => {
const data = {
value: {
errors: null,
data: {
errors: [],
warnings: []
}
}
};

it("can setStats", () => {
expect(() => dashboard.setStats(data)).not.to.throw;

dashboard.setStats(data);
expect(dashboard.logText.log).to.have.been.called;
expect(dashboard.modulesMenu.setLabel)
.to.have.been.calledWith(chalk.yellow("Modules (loading...)"));
expect(dashboard.assets.setLabel)
.to.have.been.calledWith(chalk.yellow("Assets (loading...)"));
expect(dashboard.problemsMenu.setLabel)
.to.have.been.calledWith(chalk.yellow("Problems (loading...)"));
});

it("should display stats errors if present", () => {
data.value.errors = ["error"];
expect(() => dashboard.setStats(data)).not.to.throw;

dashboard.setStats(data);
expect(dashboard.status.setContent)
.to.have.been.calledWith("{red-fg}{bold}Failed{/}");
});
});

describe("setSizes", () => {
const data = {
value: {
assets: {
foo: {
meta: {
full: 456
},
files: [{
size: {
full: 123
},
fileName: "test.js",
baseName: "/home/bar/test.js"
}]
},
bar: {
meta: {
full: 123
},
files: []
}
}
}
};

const formattedData = [
["Name", "Size"],
["foo", "456 B"],
["bar", "123 B"],
["Total", "579 B"]
];

it("can setSizes", () => {
expect(() => dashboard.setSizes(data)).to.not.throw;

dashboard.setSizes(data);
expect(dashboard.assets.setLabel).to.have.been.calledWith("Assets");
expect(dashboard.assetTable.setData).to.have.been.calledWith(formattedData);
expect(dashboard.modulesMenu.setLabel).to.have.been.calledWith("Modules");
expect(dashboard.modulesMenu.setItems).to.have.been.called;
expect(dashboard.modulesMenu.selectTab)
.to.have.been.calledWith(dashboard.modulesMenu.selected);
expect(dashboard.screen.render).to.have.been.called;
});

it("should call formatModules", () => {
// Mock out the call to setItems to force call of formatModules.
mockSetItems();
// Discard generic dashboard, create a new one with adjusted mocks.
dashboard = new Dashboard();
expect(() => dashboard.setSizes(data)).to.not.throw;
});
});

describe("setSizesError", () => {
const err = "error";

it("can setSizesError", () => {
expect(() => dashboard.setSizesError(err)).to.not.throw;

dashboard.setSizesError(err);
expect(dashboard.modulesMenu.setLabel)
.to.have.been.calledWith(chalk.red("Modules (error)"));
expect(dashboard.assets.setLabel).to.have.been.calledWith(chalk.red("Assets (error)"));
expect(dashboard.logText.log)
.to.have.been.calledWith(chalk.red("Could not load module/asset sizes."));
expect(dashboard.logText.log).to.have.been.calledWith(chalk.red(err));
});
});

describe("setProblems", () => {
const data = {
value: {
duplicates: {
assets: {
foo: "foo",
bar: "bar"
}
},
versions: {
assets: {
foo: "1.2.3",
bar: "3.2.1"
}
}
}
};

it("can setProblems", () => {
expect(() => dashboard.setProblems(data)).to.not.throw;

dashboard.setProblems(data);
expect(dashboard.problemsMenu.setLabel).to.have.been.calledWith("Problems");
expect(dashboard.problemsMenu.setItems).to.have.been.called;
expect(dashboard.problemsMenu.selectTab)
.to.have.been.calledWith(dashboard.problemsMenu.selected);
expect(dashboard.screen.render).to.have.been.called;
});

it("should call formatProblems", () => {
// Mock out the call to setItems to force call of formatProblems.
mockSetItems();
// Discard generic dashboard, create a new one with adjusted mocks.

dashboard = new Dashboard();
expect(() => dashboard.setProblems(data)).to.not.throw;
});
});

describe("setProblemsError", () => {
const err = { stack: "stack" };

it("can setProblemsError", () => {
expect(() => dashboard.setProblemsError(err)).to.not.throw;

dashboard.setProblemsError(err);
expect(dashboard.problemsMenu.setLabel)
.to.have.been.calledWith(chalk.red("Problems (error)"));
expect(dashboard.logText.log)
.to.have.been.calledWith(chalk.red("Could not analyze bundle problems."));
expect(dashboard.logText.log).to.have.been.calledWith(chalk.red(err.stack));
});
});

describe("setLog", () => {
const data = { value: "[{ log: 'log' }]" };

it("can setLog", () => {
expect(() => dashboard.setLog(data)).not.to.throw;

dashboard.setLog(data);
expect(dashboard.logText.log).to.have.been.calledWith("[ log: 'log' ]");
});

it("should return early if the stats object has errors", () => {
dashboard.stats = {};
dashboard.stats.hasErrors = () => true;
expect(dashboard.setLog(data)).to.be.undefined;

dashboard.setLog(data);
expect(dashboard.logText.log).to.not.have.been.called;
});
});
});
});
Loading