summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/unit/content-src/components/ComponentPerfTimer.test.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/unit/content-src/components/ComponentPerfTimer.test.jsx')
-rw-r--r--browser/components/newtab/test/unit/content-src/components/ComponentPerfTimer.test.jsx447
1 files changed, 447 insertions, 0 deletions
diff --git a/browser/components/newtab/test/unit/content-src/components/ComponentPerfTimer.test.jsx b/browser/components/newtab/test/unit/content-src/components/ComponentPerfTimer.test.jsx
new file mode 100644
index 0000000000..baf203947e
--- /dev/null
+++ b/browser/components/newtab/test/unit/content-src/components/ComponentPerfTimer.test.jsx
@@ -0,0 +1,447 @@
+import {
+ actionCreators as ac,
+ actionTypes as at,
+} from "common/Actions.sys.mjs";
+import { ComponentPerfTimer } from "content-src/components/ComponentPerfTimer/ComponentPerfTimer";
+import createMockRaf from "mock-raf";
+import React from "react";
+
+import { shallow } from "enzyme";
+
+const perfSvc = {
+ mark() {},
+ getMostRecentAbsMarkStartByName() {},
+};
+
+let DEFAULT_PROPS = {
+ initialized: true,
+ rows: [],
+ id: "highlights",
+ dispatch() {},
+ perfSvc,
+};
+
+describe("<ComponentPerfTimer>", () => {
+ let mockRaf;
+ let sandbox;
+ let wrapper;
+
+ const InnerEl = () => <div>Inner Element</div>;
+
+ beforeEach(() => {
+ mockRaf = createMockRaf();
+ sandbox = sinon.createSandbox();
+ sandbox.stub(window, "requestAnimationFrame").callsFake(mockRaf.raf);
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ });
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ it("should render props.children", () => {
+ assert.ok(wrapper.contains(<InnerEl />));
+ });
+
+ describe("#constructor", () => {
+ beforeEach(() => {
+ sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent");
+ sandbox.stub(
+ ComponentPerfTimer.prototype,
+ "_ensureFirstRenderTsRecorded"
+ );
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>,
+ { disableLifecycleMethods: true }
+ );
+ });
+
+ it("should have the correct defaults", () => {
+ const instance = wrapper.instance();
+
+ assert.isFalse(instance._reportMissingData);
+ assert.isFalse(instance._timestampHandled);
+ assert.isFalse(instance._recordedFirstRender);
+ });
+ });
+
+ describe("#render", () => {
+ beforeEach(() => {
+ sandbox.stub(DEFAULT_PROPS, "id").value("fake_section");
+ sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent");
+ sandbox.stub(
+ ComponentPerfTimer.prototype,
+ "_ensureFirstRenderTsRecorded"
+ );
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ });
+
+ it("should not call telemetry on sections that we don't want to record", () => {
+ const instance = wrapper.instance();
+
+ assert.notCalled(instance._maybeSendBadStateEvent);
+ assert.notCalled(instance._ensureFirstRenderTsRecorded);
+ });
+ });
+
+ describe("#_componentDidMount", () => {
+ it("should call _maybeSendPaintedEvent", () => {
+ const instance = wrapper.instance();
+ const stub = sandbox.stub(instance, "_maybeSendPaintedEvent");
+
+ instance.componentDidMount();
+
+ assert.calledOnce(stub);
+ });
+
+ it("should not call _maybeSendPaintedEvent if id not in RECORDED_SECTIONS", () => {
+ sandbox.stub(DEFAULT_PROPS, "id").value("topstories");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ const instance = wrapper.instance();
+ const stub = sandbox.stub(instance, "_maybeSendPaintedEvent");
+
+ instance.componentDidMount();
+
+ assert.notCalled(stub);
+ });
+ });
+
+ describe("#_componentDidUpdate", () => {
+ it("should call _maybeSendPaintedEvent", () => {
+ const instance = wrapper.instance();
+ const maybeSendPaintStub = sandbox.stub(
+ instance,
+ "_maybeSendPaintedEvent"
+ );
+
+ instance.componentDidUpdate();
+
+ assert.calledOnce(maybeSendPaintStub);
+ });
+
+ it("should not call _maybeSendPaintedEvent if id not in RECORDED_SECTIONS", () => {
+ sandbox.stub(DEFAULT_PROPS, "id").value("topstories");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ const instance = wrapper.instance();
+ const stub = sandbox.stub(instance, "_maybeSendPaintedEvent");
+
+ instance.componentDidUpdate();
+
+ assert.notCalled(stub);
+ });
+ });
+
+ describe("_ensureFirstRenderTsRecorded", () => {
+ let recordFirstRenderStub;
+ beforeEach(() => {
+ sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent");
+ recordFirstRenderStub = sandbox.stub(
+ ComponentPerfTimer.prototype,
+ "_ensureFirstRenderTsRecorded"
+ );
+ });
+
+ it("should set _recordedFirstRender", () => {
+ sandbox.stub(DEFAULT_PROPS, "initialized").value(false);
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ const instance = wrapper.instance();
+
+ assert.isFalse(instance._recordedFirstRender);
+
+ recordFirstRenderStub.callThrough();
+ instance._ensureFirstRenderTsRecorded();
+
+ assert.isTrue(instance._recordedFirstRender);
+ });
+
+ it("should mark first_render_ts", () => {
+ sandbox.stub(DEFAULT_PROPS, "initialized").value(false);
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ const instance = wrapper.instance();
+ const stub = sandbox.stub(perfSvc, "mark");
+
+ recordFirstRenderStub.callThrough();
+ instance._ensureFirstRenderTsRecorded();
+
+ assert.calledOnce(stub);
+ assert.calledWithExactly(stub, `${DEFAULT_PROPS.id}_first_render_ts`);
+ });
+ });
+
+ describe("#_maybeSendBadStateEvent", () => {
+ let sendBadStateStub;
+ beforeEach(() => {
+ sendBadStateStub = sandbox.stub(
+ ComponentPerfTimer.prototype,
+ "_maybeSendBadStateEvent"
+ );
+ sandbox.stub(
+ ComponentPerfTimer.prototype,
+ "_ensureFirstRenderTsRecorded"
+ );
+ });
+
+ it("should set this._reportMissingData=true when called with initialized === false", () => {
+ sandbox.stub(DEFAULT_PROPS, "initialized").value(false);
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ const instance = wrapper.instance();
+
+ assert.isFalse(instance._reportMissingData);
+
+ sendBadStateStub.callThrough();
+ instance._maybeSendBadStateEvent();
+
+ assert.isTrue(instance._reportMissingData);
+ });
+
+ it("should call _sendBadStateEvent if initialized & other metrics have been recorded", () => {
+ const instance = wrapper.instance();
+ const stub = sandbox.stub(instance, "_sendBadStateEvent");
+ instance._reportMissingData = true;
+ instance._timestampHandled = true;
+ instance._recordedFirstRender = true;
+
+ sendBadStateStub.callThrough();
+ instance._maybeSendBadStateEvent();
+
+ assert.calledOnce(stub);
+ assert.isFalse(instance._reportMissingData);
+ });
+ });
+
+ describe("#_maybeSendPaintedEvent", () => {
+ it("should call _sendPaintedEvent if props.initialized is true", () => {
+ sandbox.stub(DEFAULT_PROPS, "initialized").value(true);
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>,
+ { disableLifecycleMethods: true }
+ );
+ const instance = wrapper.instance();
+ const stub = sandbox.stub(instance, "_afterFramePaint");
+
+ assert.isFalse(instance._timestampHandled);
+
+ instance._maybeSendPaintedEvent();
+
+ assert.calledOnce(stub);
+ assert.calledWithExactly(stub, instance._sendPaintedEvent);
+ assert.isTrue(wrapper.instance()._timestampHandled);
+ });
+ it("should not call _sendPaintedEvent if this._timestampHandled is true", () => {
+ const instance = wrapper.instance();
+ const spy = sinon.spy(instance, "_afterFramePaint");
+ instance._timestampHandled = true;
+
+ instance._maybeSendPaintedEvent();
+ spy.neverCalledWith(instance._sendPaintedEvent);
+ });
+ it("should not call _sendPaintedEvent if component not initialized", () => {
+ sandbox.stub(DEFAULT_PROPS, "initialized").value(false);
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+ const instance = wrapper.instance();
+ const spy = sinon.spy(instance, "_afterFramePaint");
+
+ instance._maybeSendPaintedEvent();
+
+ spy.neverCalledWith(instance._sendPaintedEvent);
+ });
+ });
+
+ describe("#_afterFramePaint", () => {
+ it("should call callback after the requestAnimationFrame callback returns", () =>
+ new Promise(resolve => {
+ // Setting the callback to resolve is the test that it does finally get
+ // called at the correct time, after the event loop ticks again.
+ // If it doesn't get called, this test will time out.
+ const callback = sandbox.spy(resolve);
+
+ const instance = wrapper.instance();
+
+ instance._afterFramePaint(callback);
+
+ assert.notCalled(callback);
+ mockRaf.step({ count: 1 });
+ }));
+ });
+
+ describe("#_sendBadStateEvent", () => {
+ it("should call perfSvc.mark", () => {
+ sandbox.spy(perfSvc, "mark");
+ const key = `${DEFAULT_PROPS.id}_data_ready_ts`;
+
+ wrapper.instance()._sendBadStateEvent();
+
+ assert.calledOnce(perfSvc.mark);
+ assert.calledWithExactly(perfSvc.mark, key);
+ });
+
+ it("should call compute the delta from first render to data ready", () => {
+ sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName");
+
+ wrapper
+ .instance()
+ ._sendBadStateEvent(`${DEFAULT_PROPS.id}_data_ready_ts`);
+
+ assert.calledTwice(perfSvc.getMostRecentAbsMarkStartByName);
+ assert.calledWithExactly(
+ perfSvc.getMostRecentAbsMarkStartByName,
+ `${DEFAULT_PROPS.id}_data_ready_ts`
+ );
+ assert.calledWithExactly(
+ perfSvc.getMostRecentAbsMarkStartByName,
+ `${DEFAULT_PROPS.id}_first_render_ts`
+ );
+ });
+
+ it("should call dispatch SAVE_SESSION_PERF_DATA", () => {
+ sandbox
+ .stub(perfSvc, "getMostRecentAbsMarkStartByName")
+ .withArgs("highlights_first_render_ts")
+ .returns(0.5)
+ .withArgs("highlights_data_ready_ts")
+ .returns(3.2);
+
+ const dispatch = sandbox.spy(DEFAULT_PROPS, "dispatch");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+
+ wrapper.instance()._sendBadStateEvent();
+
+ assert.calledOnce(dispatch);
+ assert.calledWithExactly(
+ dispatch,
+ ac.OnlyToMain({
+ type: at.SAVE_SESSION_PERF_DATA,
+ data: { [`${DEFAULT_PROPS.id}_data_late_by_ms`]: 2 },
+ })
+ );
+ });
+ });
+
+ describe("#_sendPaintedEvent", () => {
+ beforeEach(() => {
+ sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent");
+ sandbox.stub(
+ ComponentPerfTimer.prototype,
+ "_ensureFirstRenderTsRecorded"
+ );
+ });
+
+ it("should not call mark with the wrong id", () => {
+ sandbox.stub(perfSvc, "mark");
+ sandbox.stub(DEFAULT_PROPS, "id").value("fake_id");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+
+ wrapper.instance()._sendPaintedEvent();
+
+ assert.notCalled(perfSvc.mark);
+ });
+ it("should call mark with the correct topsites", () => {
+ sandbox.stub(perfSvc, "mark");
+ sandbox.stub(DEFAULT_PROPS, "id").value("topsites");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+
+ wrapper.instance()._sendPaintedEvent();
+
+ assert.calledOnce(perfSvc.mark);
+ assert.calledWithExactly(perfSvc.mark, "topsites_first_painted_ts");
+ });
+ it("should not call getMostRecentAbsMarkStartByName if id!=topsites", () => {
+ sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName");
+ sandbox.stub(DEFAULT_PROPS, "id").value("fake_id");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+
+ wrapper.instance()._sendPaintedEvent();
+
+ assert.notCalled(perfSvc.getMostRecentAbsMarkStartByName);
+ });
+ it("should call getMostRecentAbsMarkStartByName for topsites", () => {
+ sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName");
+ sandbox.stub(DEFAULT_PROPS, "id").value("topsites");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+
+ wrapper.instance()._sendPaintedEvent();
+
+ assert.calledOnce(perfSvc.getMostRecentAbsMarkStartByName);
+ assert.calledWithExactly(
+ perfSvc.getMostRecentAbsMarkStartByName,
+ "topsites_first_painted_ts"
+ );
+ });
+ it("should dispatch SAVE_SESSION_PERF_DATA", () => {
+ sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName").returns(42);
+ sandbox.stub(DEFAULT_PROPS, "id").value("topsites");
+ const dispatch = sandbox.spy(DEFAULT_PROPS, "dispatch");
+ wrapper = shallow(
+ <ComponentPerfTimer {...DEFAULT_PROPS}>
+ <InnerEl />
+ </ComponentPerfTimer>
+ );
+
+ wrapper.instance()._sendPaintedEvent();
+
+ assert.calledOnce(dispatch);
+ assert.calledWithExactly(
+ dispatch,
+ ac.OnlyToMain({
+ type: at.SAVE_SESSION_PERF_DATA,
+ data: { topsites_first_painted_ts: 42 },
+ })
+ );
+ });
+ });
+});