ComponentPerfTimer.test.jsx (13417B)
1 import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; 2 import { ComponentPerfTimer } from "content-src/components/ComponentPerfTimer/ComponentPerfTimer"; 3 import createMockRaf from "mock-raf"; 4 import React from "react"; 5 6 import { shallow } from "enzyme"; 7 8 const perfSvc = { 9 mark() {}, 10 getMostRecentAbsMarkStartByName() {}, 11 }; 12 13 let DEFAULT_PROPS = { 14 initialized: true, 15 rows: [], 16 id: "highlights", 17 dispatch() {}, 18 perfSvc, 19 }; 20 21 describe("<ComponentPerfTimer>", () => { 22 let mockRaf; 23 let sandbox; 24 let wrapper; 25 26 const InnerEl = () => <div>Inner Element</div>; 27 28 beforeEach(() => { 29 mockRaf = createMockRaf(); 30 sandbox = sinon.createSandbox(); 31 sandbox.stub(window, "requestAnimationFrame").callsFake(mockRaf.raf); 32 wrapper = shallow( 33 <ComponentPerfTimer {...DEFAULT_PROPS}> 34 <InnerEl /> 35 </ComponentPerfTimer> 36 ); 37 }); 38 afterEach(() => { 39 sandbox.restore(); 40 }); 41 42 it("should render props.children", () => { 43 assert.ok(wrapper.contains(<InnerEl />)); 44 }); 45 46 describe("#constructor", () => { 47 beforeEach(() => { 48 sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent"); 49 sandbox.stub( 50 ComponentPerfTimer.prototype, 51 "_ensureFirstRenderTsRecorded" 52 ); 53 wrapper = shallow( 54 <ComponentPerfTimer {...DEFAULT_PROPS}> 55 <InnerEl /> 56 </ComponentPerfTimer>, 57 { disableLifecycleMethods: true } 58 ); 59 }); 60 61 it("should have the correct defaults", () => { 62 const instance = wrapper.instance(); 63 64 assert.isFalse(instance._reportMissingData); 65 assert.isFalse(instance._timestampHandled); 66 assert.isFalse(instance._recordedFirstRender); 67 }); 68 }); 69 70 describe("#render", () => { 71 beforeEach(() => { 72 sandbox.stub(DEFAULT_PROPS, "id").value("fake_section"); 73 sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent"); 74 sandbox.stub( 75 ComponentPerfTimer.prototype, 76 "_ensureFirstRenderTsRecorded" 77 ); 78 wrapper = shallow( 79 <ComponentPerfTimer {...DEFAULT_PROPS}> 80 <InnerEl /> 81 </ComponentPerfTimer> 82 ); 83 }); 84 85 it("should not call telemetry on sections that we don't want to record", () => { 86 const instance = wrapper.instance(); 87 88 assert.notCalled(instance._maybeSendBadStateEvent); 89 assert.notCalled(instance._ensureFirstRenderTsRecorded); 90 }); 91 }); 92 93 describe("#_componentDidMount", () => { 94 it("should call _maybeSendPaintedEvent", () => { 95 const instance = wrapper.instance(); 96 const stub = sandbox.stub(instance, "_maybeSendPaintedEvent"); 97 98 instance.componentDidMount(); 99 100 assert.calledOnce(stub); 101 }); 102 103 it("should not call _maybeSendPaintedEvent if id not in RECORDED_SECTIONS", () => { 104 sandbox.stub(DEFAULT_PROPS, "id").value("topstories"); 105 wrapper = shallow( 106 <ComponentPerfTimer {...DEFAULT_PROPS}> 107 <InnerEl /> 108 </ComponentPerfTimer> 109 ); 110 const instance = wrapper.instance(); 111 const stub = sandbox.stub(instance, "_maybeSendPaintedEvent"); 112 113 instance.componentDidMount(); 114 115 assert.notCalled(stub); 116 }); 117 }); 118 119 describe("#_componentDidUpdate", () => { 120 it("should call _maybeSendPaintedEvent", () => { 121 const instance = wrapper.instance(); 122 const maybeSendPaintStub = sandbox.stub( 123 instance, 124 "_maybeSendPaintedEvent" 125 ); 126 127 instance.componentDidUpdate(); 128 129 assert.calledOnce(maybeSendPaintStub); 130 }); 131 132 it("should not call _maybeSendPaintedEvent if id not in RECORDED_SECTIONS", () => { 133 sandbox.stub(DEFAULT_PROPS, "id").value("topstories"); 134 wrapper = shallow( 135 <ComponentPerfTimer {...DEFAULT_PROPS}> 136 <InnerEl /> 137 </ComponentPerfTimer> 138 ); 139 const instance = wrapper.instance(); 140 const stub = sandbox.stub(instance, "_maybeSendPaintedEvent"); 141 142 instance.componentDidUpdate(); 143 144 assert.notCalled(stub); 145 }); 146 }); 147 148 describe("_ensureFirstRenderTsRecorded", () => { 149 let recordFirstRenderStub; 150 beforeEach(() => { 151 sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent"); 152 recordFirstRenderStub = sandbox.stub( 153 ComponentPerfTimer.prototype, 154 "_ensureFirstRenderTsRecorded" 155 ); 156 }); 157 158 it("should set _recordedFirstRender", () => { 159 sandbox.stub(DEFAULT_PROPS, "initialized").value(false); 160 wrapper = shallow( 161 <ComponentPerfTimer {...DEFAULT_PROPS}> 162 <InnerEl /> 163 </ComponentPerfTimer> 164 ); 165 const instance = wrapper.instance(); 166 167 assert.isFalse(instance._recordedFirstRender); 168 169 recordFirstRenderStub.callThrough(); 170 instance._ensureFirstRenderTsRecorded(); 171 172 assert.isTrue(instance._recordedFirstRender); 173 }); 174 175 it("should mark first_render_ts", () => { 176 sandbox.stub(DEFAULT_PROPS, "initialized").value(false); 177 wrapper = shallow( 178 <ComponentPerfTimer {...DEFAULT_PROPS}> 179 <InnerEl /> 180 </ComponentPerfTimer> 181 ); 182 const instance = wrapper.instance(); 183 const stub = sandbox.stub(perfSvc, "mark"); 184 185 recordFirstRenderStub.callThrough(); 186 instance._ensureFirstRenderTsRecorded(); 187 188 assert.calledOnce(stub); 189 assert.calledWithExactly(stub, `${DEFAULT_PROPS.id}_first_render_ts`); 190 }); 191 }); 192 193 describe("#_maybeSendBadStateEvent", () => { 194 let sendBadStateStub; 195 beforeEach(() => { 196 sendBadStateStub = sandbox.stub( 197 ComponentPerfTimer.prototype, 198 "_maybeSendBadStateEvent" 199 ); 200 sandbox.stub( 201 ComponentPerfTimer.prototype, 202 "_ensureFirstRenderTsRecorded" 203 ); 204 }); 205 206 it("should set this._reportMissingData=true when called with initialized === false", () => { 207 sandbox.stub(DEFAULT_PROPS, "initialized").value(false); 208 wrapper = shallow( 209 <ComponentPerfTimer {...DEFAULT_PROPS}> 210 <InnerEl /> 211 </ComponentPerfTimer> 212 ); 213 const instance = wrapper.instance(); 214 215 assert.isFalse(instance._reportMissingData); 216 217 sendBadStateStub.callThrough(); 218 instance._maybeSendBadStateEvent(); 219 220 assert.isTrue(instance._reportMissingData); 221 }); 222 223 it("should call _sendBadStateEvent if initialized & other metrics have been recorded", () => { 224 const instance = wrapper.instance(); 225 const stub = sandbox.stub(instance, "_sendBadStateEvent"); 226 instance._reportMissingData = true; 227 instance._timestampHandled = true; 228 instance._recordedFirstRender = true; 229 230 sendBadStateStub.callThrough(); 231 instance._maybeSendBadStateEvent(); 232 233 assert.calledOnce(stub); 234 assert.isFalse(instance._reportMissingData); 235 }); 236 }); 237 238 describe("#_maybeSendPaintedEvent", () => { 239 it("should call _sendPaintedEvent if props.initialized is true", () => { 240 sandbox.stub(DEFAULT_PROPS, "initialized").value(true); 241 wrapper = shallow( 242 <ComponentPerfTimer {...DEFAULT_PROPS}> 243 <InnerEl /> 244 </ComponentPerfTimer>, 245 { disableLifecycleMethods: true } 246 ); 247 const instance = wrapper.instance(); 248 const stub = sandbox.stub(instance, "_afterFramePaint"); 249 250 assert.isFalse(instance._timestampHandled); 251 252 instance._maybeSendPaintedEvent(); 253 254 assert.calledOnce(stub); 255 assert.calledWithExactly(stub, instance._sendPaintedEvent); 256 assert.isTrue(wrapper.instance()._timestampHandled); 257 }); 258 it("should not call _sendPaintedEvent if this._timestampHandled is true", () => { 259 const instance = wrapper.instance(); 260 const spy = sinon.spy(instance, "_afterFramePaint"); 261 instance._timestampHandled = true; 262 263 instance._maybeSendPaintedEvent(); 264 spy.neverCalledWith(instance._sendPaintedEvent); 265 }); 266 it("should not call _sendPaintedEvent if component not initialized", () => { 267 sandbox.stub(DEFAULT_PROPS, "initialized").value(false); 268 wrapper = shallow( 269 <ComponentPerfTimer {...DEFAULT_PROPS}> 270 <InnerEl /> 271 </ComponentPerfTimer> 272 ); 273 const instance = wrapper.instance(); 274 const spy = sinon.spy(instance, "_afterFramePaint"); 275 276 instance._maybeSendPaintedEvent(); 277 278 spy.neverCalledWith(instance._sendPaintedEvent); 279 }); 280 }); 281 282 describe("#_afterFramePaint", () => { 283 it("should call callback after the requestAnimationFrame callback returns", () => 284 new Promise(resolve => { 285 // Setting the callback to resolve is the test that it does finally get 286 // called at the correct time, after the event loop ticks again. 287 // If it doesn't get called, this test will time out. 288 const callback = sandbox.spy(resolve); 289 290 const instance = wrapper.instance(); 291 292 instance._afterFramePaint(callback); 293 294 assert.notCalled(callback); 295 mockRaf.step({ count: 1 }); 296 })); 297 }); 298 299 describe("#_sendBadStateEvent", () => { 300 it("should call perfSvc.mark", () => { 301 sandbox.spy(perfSvc, "mark"); 302 const key = `${DEFAULT_PROPS.id}_data_ready_ts`; 303 304 wrapper.instance()._sendBadStateEvent(); 305 306 assert.calledOnce(perfSvc.mark); 307 assert.calledWithExactly(perfSvc.mark, key); 308 }); 309 310 it("should call compute the delta from first render to data ready", () => { 311 sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName"); 312 313 wrapper 314 .instance() 315 ._sendBadStateEvent(`${DEFAULT_PROPS.id}_data_ready_ts`); 316 317 assert.calledTwice(perfSvc.getMostRecentAbsMarkStartByName); 318 assert.calledWithExactly( 319 perfSvc.getMostRecentAbsMarkStartByName, 320 `${DEFAULT_PROPS.id}_data_ready_ts` 321 ); 322 assert.calledWithExactly( 323 perfSvc.getMostRecentAbsMarkStartByName, 324 `${DEFAULT_PROPS.id}_first_render_ts` 325 ); 326 }); 327 328 it("should call dispatch SAVE_SESSION_PERF_DATA", () => { 329 sandbox 330 .stub(perfSvc, "getMostRecentAbsMarkStartByName") 331 .withArgs("highlights_first_render_ts") 332 .returns(0.5) 333 .withArgs("highlights_data_ready_ts") 334 .returns(3.2); 335 336 const dispatch = sandbox.spy(DEFAULT_PROPS, "dispatch"); 337 wrapper = shallow( 338 <ComponentPerfTimer {...DEFAULT_PROPS}> 339 <InnerEl /> 340 </ComponentPerfTimer> 341 ); 342 343 wrapper.instance()._sendBadStateEvent(); 344 345 assert.calledOnce(dispatch); 346 assert.calledWithExactly( 347 dispatch, 348 ac.OnlyToMain({ 349 type: at.SAVE_SESSION_PERF_DATA, 350 data: { [`${DEFAULT_PROPS.id}_data_late_by_ms`]: 2 }, 351 }) 352 ); 353 }); 354 }); 355 356 describe("#_sendPaintedEvent", () => { 357 beforeEach(() => { 358 sandbox.stub(ComponentPerfTimer.prototype, "_maybeSendBadStateEvent"); 359 sandbox.stub( 360 ComponentPerfTimer.prototype, 361 "_ensureFirstRenderTsRecorded" 362 ); 363 }); 364 365 it("should not call mark with the wrong id", () => { 366 sandbox.stub(perfSvc, "mark"); 367 sandbox.stub(DEFAULT_PROPS, "id").value("fake_id"); 368 wrapper = shallow( 369 <ComponentPerfTimer {...DEFAULT_PROPS}> 370 <InnerEl /> 371 </ComponentPerfTimer> 372 ); 373 374 wrapper.instance()._sendPaintedEvent(); 375 376 assert.notCalled(perfSvc.mark); 377 }); 378 it("should call mark with the correct topsites", () => { 379 sandbox.stub(perfSvc, "mark"); 380 sandbox.stub(DEFAULT_PROPS, "id").value("topsites"); 381 wrapper = shallow( 382 <ComponentPerfTimer {...DEFAULT_PROPS}> 383 <InnerEl /> 384 </ComponentPerfTimer> 385 ); 386 387 wrapper.instance()._sendPaintedEvent(); 388 389 assert.calledOnce(perfSvc.mark); 390 assert.calledWithExactly(perfSvc.mark, "topsites_first_painted_ts"); 391 }); 392 it("should not call getMostRecentAbsMarkStartByName if id!=topsites", () => { 393 sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName"); 394 sandbox.stub(DEFAULT_PROPS, "id").value("fake_id"); 395 wrapper = shallow( 396 <ComponentPerfTimer {...DEFAULT_PROPS}> 397 <InnerEl /> 398 </ComponentPerfTimer> 399 ); 400 401 wrapper.instance()._sendPaintedEvent(); 402 403 assert.notCalled(perfSvc.getMostRecentAbsMarkStartByName); 404 }); 405 it("should call getMostRecentAbsMarkStartByName for topsites", () => { 406 sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName"); 407 sandbox.stub(DEFAULT_PROPS, "id").value("topsites"); 408 wrapper = shallow( 409 <ComponentPerfTimer {...DEFAULT_PROPS}> 410 <InnerEl /> 411 </ComponentPerfTimer> 412 ); 413 414 wrapper.instance()._sendPaintedEvent(); 415 416 assert.calledOnce(perfSvc.getMostRecentAbsMarkStartByName); 417 assert.calledWithExactly( 418 perfSvc.getMostRecentAbsMarkStartByName, 419 "topsites_first_painted_ts" 420 ); 421 }); 422 it("should dispatch SAVE_SESSION_PERF_DATA", () => { 423 sandbox.stub(perfSvc, "getMostRecentAbsMarkStartByName").returns(42); 424 sandbox.stub(DEFAULT_PROPS, "id").value("topsites"); 425 const dispatch = sandbox.spy(DEFAULT_PROPS, "dispatch"); 426 wrapper = shallow( 427 <ComponentPerfTimer {...DEFAULT_PROPS}> 428 <InnerEl /> 429 </ComponentPerfTimer> 430 ); 431 432 wrapper.instance()._sendPaintedEvent(); 433 434 assert.calledOnce(dispatch); 435 assert.calledWithExactly( 436 dispatch, 437 ac.OnlyToMain({ 438 type: at.SAVE_SESSION_PERF_DATA, 439 data: { topsites_first_painted_ts: 42 }, 440 }) 441 ); 442 }); 443 }); 444 });