60c681bd93334552916711bbbe167bc4bd588473
[WebKit-https.git] / LayoutTests / resize-observer / resources / resizeTestHelper.js
1 'use strict';
2
3 /**
4   ResizeTestHelper is a framework to test ResizeObserver
5   notifications. Use it to make assertions about ResizeObserverEntries.
6   This framework is needed because ResizeObservations are
7   delivered asynchronously inside the event loop.
8
9   Features:
10   - can queue multiple notification steps in a test
11   - handles timeouts
12   - returns Promise that is fullfilled when test completes.
13     Use to chain tests (since parallel async ResizeObserver tests
14     would conflict if reusing same DOM elements).
15
16   Usage:
17
18   create ResizeTestHelper for every test.
19   Make assertions inside notify, timeout callbacks.
20   Start tests with helper.start()
21   Chain tests with Promises.
22   Counts animation frames, see startCountingRaf
23 */
24
25 /*
26   @param name: test name
27   @param steps:
28   {
29     setup: function(ResizeObserver) {
30       // called at the beginning of the test step
31       // your observe/resize code goes here
32     },
33     notify: function(entries, observer) {
34       // ResizeObserver callback.
35       // Make assertions here.
36       // Return true if next step should start on the next event loop.
37     },
38     timeout: function() {
39       // Define this if your test expects to time out.
40       // If undefined, timeout is assert_unreached.
41     }
42   }
43 */
44 function ResizeTestHelper(name, steps)
45 {
46     this._name = name;
47     this._steps = steps || [];
48     this._stepIdx = -1;
49     this._harnessTest = null;
50     this._observer = new ResizeObserver(this._handleNotification.bind(this));
51     this._timeoutBind = this._handleTimeout.bind(this);
52     this._nextStepBind = this._nextStep.bind(this);
53 }
54
55 ResizeTestHelper.TIMEOUT = 100;
56
57 ResizeTestHelper.prototype = {
58   get _currentStep() {
59     return this._steps[this._stepIdx];
60   },
61
62   _nextStep: function() {
63     if (++this._stepIdx == this._steps.length)
64       return this._done();
65     this._timeoutId = this._harnessTest.step_timeout(
66       this._timeoutBind, ResizeTestHelper.TIMEOUT);
67     try {
68       this._steps[this._stepIdx].setup(this._observer);
69     }
70     catch(err) {
71       this._harnessTest.step(() => {
72         assert_unreached("Caught a throw, possible syntax error");
73       });
74     }
75   },
76
77   _handleNotification: function(entries) {
78     if (this._timeoutId) {
79       window.clearTimeout(this._timeoutId);
80       delete this._timeoutId;
81     }
82     this._harnessTest.step(() => {
83       let rafDelay = this._currentStep.notify(entries, this._observer);
84       if (rafDelay)
85         window.requestAnimationFrame(this._nextStepBind);
86       else
87         this._nextStep();
88     });
89   },
90
91   _handleTimeout: function() {
92     delete this._timeoutId;
93     this._harnessTest.step(() => {
94       if (this._currentStep.timeout) {
95         this._currentStep.timeout();
96       }
97       else {
98         assert_unreached("Timed out waiting for notification. (" + ResizeTestHelper.TIMEOUT + "ms)");
99       }
100       this._nextStep();
101     });
102   },
103
104   _done: function() {
105     this._observer.disconnect();
106     delete this._observer;
107     this._harnessTest.done();
108     if (this._rafCountRequest) {
109       window.cancelAnimationFrame(this._rafCountRequest);
110       delete this._rafCountRequest;
111     }
112     window.requestAnimationFrame(() => { this._resolvePromise(); });
113   },
114
115   start: function() {
116     this._harnessTest = async_test(this._name);
117     this._harnessTest.step(() => {
118       assert_equals(this._stepIdx, -1, "start can only be called once");
119       this._nextStep();
120     });
121     return new Promise( (resolve, reject) => {
122       this._resolvePromise = resolve;
123       this._rejectPromise = reject;
124     });
125   },
126
127   get rafCount() {
128     if (!this._rafCountRequest)
129       throw "rAF count is not active";
130     return this._rafCount;
131   },
132
133   _incrementRaf: function() {
134     if (this._rafCountRequest) {
135       this._rafCount++;
136       this._rafCountRequest = window.requestAnimationFrame(this._incrementRafBind);
137     }
138   },
139
140   startCountingRaf: function() {
141     if (this._rafCountRequest)
142       window.cancelAnimationFrame(this._rafCountRequest);
143     if (!this._incrementRafBind)
144       this._incrementRafBind = this._incrementRaf.bind(this);
145     this._rafCount = 0;
146     this._rafCountRequest = window.requestAnimationFrame(this._incrementRafBind);
147   }
148 }