Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

uber / deck.gl / 13779

18 Sep 2019 - 0:00 coverage decreased (-2.9%) to 79.902%
13779

Pull #3623

travis-ci-com

9181eb84f9c35729a3bad740fb7f9d93?size=18&default=identiconweb-flow
beta.2
Pull Request #3623: Bump dependency versions

3405 of 4619 branches covered (73.72%)

Branch coverage included in aggregate %.

7031 of 8442 relevant lines covered (83.29%)

5687.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.31
/modules/core/src/lifecycle/component-state.js
1
// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
9×
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a copy
4
// of this software and associated documentation files (the "Software"), to deal
5
// in the Software without restriction, including without limitation the rights
6
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
// copies of the Software, and to permit persons to whom the Software is
8
// furnished to do so, subject to the following conditions:
9
//
10
// The above copyright notice and this permission notice shall be included in
11
// all copies or substantial portions of the Software.
12
//
13
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
// THE SOFTWARE.
20

21
import log from '../utils/log';
22
import assert from '../utils/assert';
23
import {isAsyncIterable} from '../utils/iterable-utils';
24

25
const EMPTY_PROPS = Object.freeze({});
1×
26

27
export default class ComponentState {
28
  constructor(component = null) {
29
    this.component = component;
143×
30
    this.asyncProps = {}; // Prop values that the layer sees
143×
31
    this.onAsyncPropUpdated = () => {};
143×
32
    this.oldProps = EMPTY_PROPS; // Last props before update
143×
33
    this.oldAsyncProps = null; // Last props before update, with async values copied.
143×
34
  }
35

36
  getOldProps() {
37
    return this.oldAsyncProps || this.oldProps;
3,531×
38
  }
39

40
  resetOldProps() {
41
    this.oldAsyncProps = null;
1,070×
42
    this.oldProps = this.component.props;
1,070×
43
  }
44

45
  // Whenever async props are changing, we need to make a copy of oldProps
46
  // otherwise the prop rewriting will affect the value both in props and oldProps.
47
  // While the copy is relatively expensive, this only happens on load completion.
48
  freezeAsyncOldProps() {
49
    if (!this.oldAsyncProps) {
10×
50
      // Make sure oldProps is set
51
      this.oldProps = this.oldProps || this.component.props;
Branches [[3, 1]] missed. 9×
52

53
      // 1. inherit all synchronous props from oldProps
54
      // 2. reconfigure the async prop descriptors to fixed values
55
      this.oldAsyncProps = Object.create(this.oldProps);
9×
56
      for (const propName in this.asyncProps) {
9×
57
        Object.defineProperty(this.oldAsyncProps, propName, {
9×
58
          enumerable: true,
59
          value: this.oldProps[propName]
60
        });
61
      }
62
    }
63
  }
64

65
  // ASYNC PROP HANDLING
66
  //
67

68
  // Checks if a prop is overridden
69
  hasAsyncProp(propName) {
70
    return propName in this.asyncProps;
50×
71
  }
72

73
  // Returns value of an overriden prop
74
  getAsyncProp(propName) {
75
    const asyncProp = this.asyncProps[propName];
51×
76
    return asyncProp && asyncProp.resolvedValue;
51×
77
  }
78

79
  isAsyncPropLoading(propName) {
80
    const asyncProp = this.asyncProps[propName];
6×
81
    return Boolean(
6×
82
      asyncProp &&
83
        asyncProp.pendingLoadCount > 0 &&
84
        asyncProp.pendingLoadCount !== asyncProp.resolvedLoadCount
85
    );
86
  }
87

88
  // Updates all async/overridden props (when new props come in)
89
  // Checks if urls have changed, starts loading, or removes override
90
  setAsyncProps(props) {
91
    // NOTE: prop param and default values are only support for testing
92
    const resolvedValues = props._asyncPropResolvedValues || {};
1,251×
93
    const originalValues = props._asyncPropOriginalValues || props;
1,251×
94
    const defaultValues = props._asyncPropDefaultValues || {};
1,251×
95

96
    // TODO - use async props from the layer's prop types
97
    for (const propName in resolvedValues) {
1,251×
98
      const value = resolvedValues[propName];
1,268×
99
      this._createAsyncPropData(propName, value, defaultValues[propName]);
1,268×
100
      this._updateAsyncProp(propName, value);
1,268×
101
    }
102

103
    for (const propName in originalValues) {
1,251×
104
      const value = originalValues[propName];
16×
105
      // Makes sure a record exists for this prop
106
      this._createAsyncPropData(propName, value, defaultValues[propName]);
16×
107
      this._updateAsyncProp(propName, value);
16×
108
    }
109
  }
110

111
  // Intercept strings (URLs) and Promises and activates loading and prop rewriting
112
  _updateAsyncProp(propName, value) {
113
    if (!this._didAsyncInputValueChange(propName, value)) {
1,284×
114
      return;
953×
115
    }
116

117
    // interpret value string as url and start a new load tracked by a promise
118
    if (typeof value === 'string') {
Branches [[10, 0]] missed. 331×
UNCOV
119
      const fetch = this.layer && this.layer.props.fetch;
Branches [[11, 0], [11, 1]] missed. !
UNCOV
120
      const url = value;
!
UNCOV
121
      if (fetch) {
Branches [[12, 0], [12, 1]] missed. !
UNCOV
122
        value = fetch(url, {propName, layer: this.layer});
!
123
      }
124
    }
125

126
    // interprets promise and track the "loading"
127
    if (value instanceof Promise) {
331×
128
      this._watchPromise(propName, value);
8×
129
      return;
8×
130
    }
131

132
    if (isAsyncIterable(value)) {
323×
133
      this._resolveAsyncIterable(propName, value);
1×
134
      return;
1×
135
    }
136

137
    // else, normal, non-async value. Just store value for now
138
    this._setPropValue(propName, value);
322×
139
  }
140

141
  // Checks if an input value actually changed (to avoid reloading/rewatching promises/urls)
142
  _didAsyncInputValueChange(propName, value) {
143
    const asyncProp = this.asyncProps[propName];
1,284×
144
    if (value === asyncProp.lastValue) {
1,284×
145
      return false;
953×
146
    }
147
    asyncProp.lastValue = value;
331×
148
    return true;
331×
149
  }
150

151
  // Set normal, non-async value
152
  _setPropValue(propName, value) {
153
    const asyncProp = this.asyncProps[propName];
322×
154
    asyncProp.value = value;
322×
155
    asyncProp.resolvedValue = value;
322×
156
    asyncProp.pendingLoadCount++;
322×
157
    asyncProp.resolvedLoadCount = asyncProp.pendingLoadCount;
322×
158
  }
159

160
  // Set a just resolved async value, calling onAsyncPropUpdates if value changes asynchronously
161
  _setAsyncPropValue(propName, value, loadCount) {
162
    // Only update if loadCount is larger or equal to resolvedLoadCount
163
    // otherwise a more recent load has already completed
164
    const asyncProp = this.asyncProps[propName];
11×
165
    if (asyncProp && loadCount >= asyncProp.resolvedLoadCount) {
11×
166
      assert(value !== undefined);
10×
167

168
      // A chance to copy old props before updating
169
      this.freezeAsyncOldProps();
10×
170

171
      asyncProp.resolvedValue = value;
10×
172
      asyncProp.resolvedLoadCount = loadCount;
10×
173

174
      // Call callback to inform listener
175
      this.onAsyncPropUpdated(propName, value);
10×
176
    }
177
  }
178

179
  // Tracks a promise, sets the prop when loaded, handles load count
180
  _watchPromise(propName, promise) {
181
    const asyncProp = this.asyncProps[propName];
8×
182
    asyncProp.pendingLoadCount++;
8×
183
    const loadCount = asyncProp.pendingLoadCount;
8×
184
    promise
8×
185
      .then(data => {
186
        data = this._postProcessValue(propName, data);
8×
187
        this._setAsyncPropValue(propName, data, loadCount);
8×
188

189
        const onDataLoad = this.layer && this.layer.props.onDataLoad;
8×
190
        if (propName === 'data' && onDataLoad) {
8×
191
          onDataLoad(data, {propName, layer: this.layer});
1×
192
        }
193
      })
UNCOV
194
      .catch(error => log.error(error)());
!
195
  }
196

197
  async _resolveAsyncIterable(propName, iterable) {
198
    if (propName !== 'data') {
Branches [[21, 0]] missed. 1×
199
      // we only support data as async iterable
UNCOV
200
      this._setPropValue(propName, iterable);
!
201
    }
202

203
    const asyncProp = this.asyncProps[propName];
1×
204
    asyncProp.pendingLoadCount++;
1×
205
    const loadCount = asyncProp.pendingLoadCount;
1×
206
    let data = [];
1×
207
    let count = 0;
1×
208

209
    for await (const chunk of iterable) {
1×
210
      data = this._postProcessValue(propName, chunk, data);
3×
211

212
      // Used by the default _dataDiff function
213
      Object.defineProperty(data, '__diff', {
3×
214
        enumerable: false,
215
        value: [{startRow: count, endRow: data.length}]
216
      });
217

218
      count = data.length;
3×
219
      this._setAsyncPropValue(propName, data, loadCount);
3×
220
    }
221

222
    const onDataLoad = this.layer && this.layer.props.onDataLoad;
1×
223
    if (onDataLoad) {
Branches [[23, 1]] missed. 1×
224
      onDataLoad(data, {propName, layer: this.layer});
1×
225
    }
226
  }
227

228
  // Give the app a chance to post process the loaded data
229
  _postProcessValue(propName, value, previousValue) {
230
    const {dataTransform} = this.component ? this.component.props : {};
11×
231
    if (propName !== 'data') {
Branches [[25, 0]] missed. 11×
UNCOV
232
      return value;
!
233
    }
234
    if (dataTransform) {
Branches [[26, 0]] missed. 11×
UNCOV
235
      return dataTransform(value, previousValue);
!
236
    }
237
    // previousValue is assigned if loaded with async iterator
238
    return previousValue ? previousValue.concat(value) : value;
11×
239
  }
240

241
  // Creating an asyncProp record if needed
242
  _createAsyncPropData(propName, value, defaultValue) {
243
    const asyncProp = this.asyncProps[propName];
1,284×
244
    if (!asyncProp) {
1,284×
245
      // assert(defaultValue !== undefined);
246
      this.asyncProps[propName] = {
138×
247
        lastValue: null, // Supplied prop value (can be url/promise, not visible to layer)
248
        resolvedValue: defaultValue, // Resolved prop value (valid data, can be "shown" to layer)
249
        pendingLoadCount: 0, // How many loads have been issued
250
        resolvedLoadCount: 0 // Latest resolved load, (earlier loads will be ignored)
251
      };
252
    }
253
  }
254
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2019 Coveralls, LLC