• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 951400782

pending completion
951400782

Pull #3900

gitlab-ci

web-flow
chore: bump @testing-library/jest-dom from 5.16.5 to 5.17.0

Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.16.5 to 5.17.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.16.5...v5.17.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #3900: chore: bump @testing-library/jest-dom from 5.16.5 to 5.17.0

4446 of 6414 branches covered (69.32%)

8342 of 10084 relevant lines covered (82.73%)

186.0 hits per line

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

89.85
/src/js/selectors/index.js
1
// Copyright 2020 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import { createSelector } from '@reduxjs/toolkit';
15

16
import { defaultReports } from '../actions/deviceActions';
17
import { mapUserRolesToUiPermissions } from '../actions/userActions';
18
import { PLANS } from '../constants/appConstants';
19
import { DEPLOYMENT_STATES } from '../constants/deploymentConstants';
20
import {
21
  ALL_DEVICES,
22
  ATTRIBUTE_SCOPES,
23
  DEVICE_ISSUE_OPTIONS,
24
  DEVICE_LIST_MAXIMUM_LENGTH,
25
  DEVICE_ONLINE_CUTOFF,
26
  DEVICE_STATES,
27
  EXTERNAL_PROVIDER,
28
  UNGROUPED_GROUP
29
} from '../constants/deviceConstants';
30
import { rolesByName, twoFAStates, uiPermissionsById } from '../constants/userConstants';
31
import { attributeDuplicateFilter, duplicateFilter, getDemoDeviceAddress as getDemoDeviceAddressHelper, versionCompare } from '../helpers';
32

33
const getAppDocsVersion = state => state.app.docsVersion;
1,422✔
34
export const getFeatures = state => state.app.features;
16,297✔
35
export const getRolesById = state => state.users.rolesById;
1,636✔
36
export const getOrganization = state => state.organization.organization;
6,913✔
37
export const getAcceptedDevices = state => state.devices.byStatus.accepted;
7,756✔
38
const getDevicesByStatus = state => state.devices.byStatus;
1,481✔
39
export const getDevicesById = state => state.devices.byId;
9,815✔
40
export const getDeviceReports = state => state.devices.reports;
3,671✔
41
export const getGroupsById = state => state.devices.groups.byId;
3,004✔
42
const getSelectedGroup = state => state.devices.groups.selectedGroup;
187✔
43
const getSearchedDevices = state => state.app.searchState.deviceIds;
1,073✔
44
const getListedDevices = state => state.devices.deviceList.deviceIds;
187✔
45
const getFilteringAttributes = state => state.devices.filteringAttributes;
1,211✔
46
export const getDeviceFilters = state => state.devices.filters || [];
202!
47
const getFilteringAttributesFromConfig = state => state.devices.filteringAttributesConfig.attributes;
1,177✔
48
export const getSortedFilteringAttributes = createSelector([getFilteringAttributes], filteringAttributes => ({
187✔
49
  ...filteringAttributes,
50
  identityAttributes: [...filteringAttributes.identityAttributes, 'id']
51
}));
52
export const getDeviceLimit = state => state.devices.limit;
2,067✔
53
const getDevicesList = state => Object.values(state.devices.byId);
187✔
54
const getOnboarding = state => state.onboarding;
1,552✔
55
export const getShowHelptips = state => state.users.showHelptips;
5,943✔
56
export const getGlobalSettings = state => state.users.globalSettings;
3,828✔
57
const getIssueCountsByType = state => state.monitor.issueCounts.byType;
1,185✔
58
export const getReleasesById = state => state.releases.byId;
1,978✔
59
const getReleaseTags = state => state.releases.releaseTags;
187✔
60
const getListedReleases = state => state.releases.releasesList.releaseIds;
187✔
61
export const getExternalIntegrations = state => state.organization.externalDeviceIntegrations;
187✔
62
const getDeploymentsById = state => state.deployments.byId;
2,700✔
63
export const getDeploymentsByStatus = state => state.deployments.byStatus;
1,992✔
64
export const getVersionInformation = state => state.app.versionInformation;
1,228✔
65
const getCurrentUserId = state => state.users.currentUser;
2,849✔
66
const getUsersById = state => state.users.byId;
1,672✔
67
export const getCurrentUser = createSelector([getUsersById, getCurrentUserId], (usersById, userId) => usersById[userId] ?? {});
187✔
68
export const getUserSettings = state => state.users.userSettings;
7,936✔
69
export const getIsPreview = createSelector([getVersionInformation], ({ Integration }) => versionCompare(Integration, 'next') > -1);
187✔
70

71
export const getDeploymentsSelectionState = state => state.deployments.selectionState;
2,292✔
72

73
export const getMappedDeploymentSelection = createSelector(
187✔
74
  [getDeploymentsSelectionState, (_, deploymentsState) => deploymentsState, getDeploymentsById],
1,223✔
75
  (selectionState, deploymentsState, deploymentsById) => {
76
    const { selection = [] } = selectionState[deploymentsState] ?? {};
1,192!
77
    return selection.reduce((accu, id) => {
1,192✔
78
      if (deploymentsById[id]) {
2,158!
79
        accu.push(deploymentsById[id]);
2,158✔
80
      }
81
      return accu;
2,158✔
82
    }, []);
83
  }
84
);
85

86
export const getDeploymentRelease = createSelector(
187✔
87
  [getDeploymentsById, getDeploymentsSelectionState, getReleasesById],
88
  (deploymentsById, { selectedId }, releasesById) => {
89
    const deployment = deploymentsById[selectedId] || {};
154✔
90
    return deployment.artifact_name && releasesById[deployment.artifact_name] ? releasesById[deployment.artifact_name] : { device_types_compatible: [] };
154✔
91
  }
92
);
93

94
export const getHas2FA = createSelector(
187✔
95
  [getCurrentUser],
96
  currentUser => currentUser.hasOwnProperty('tfa_status') && currentUser.tfa_status === twoFAStates.enabled
5✔
97
);
98

99
export const getDemoDeviceAddress = createSelector([getDevicesList, getOnboarding], (devices, { approach, demoArtifactPort }) => {
187✔
100
  const demoDeviceAddress = `http://${getDemoDeviceAddressHelper(devices, approach)}`;
6✔
101
  return demoArtifactPort ? `${demoDeviceAddress}:${demoArtifactPort}` : demoDeviceAddress;
6!
102
});
103

104
export const getDeviceReportsForUser = createSelector(
187✔
105
  [getUserSettings, getCurrentUserId, getGlobalSettings, getDevicesById],
106
  ({ reports }, currentUserId, globalSettings, devicesById) => {
107
    return reports || globalSettings[`${currentUserId}-reports`] || (Object.keys(devicesById).length ? defaultReports : []);
52✔
108
  }
109
);
110

111
const listItemMapper = (byId, ids, { defaultObject = {}, cutOffSize = DEVICE_LIST_MAXIMUM_LENGTH }) => {
187✔
112
  return ids.slice(0, cutOffSize).reduce((accu, id) => {
1,133✔
113
    if (id && byId[id]) {
249!
114
      accu.push({ ...defaultObject, ...byId[id] });
249✔
115
    }
116
    return accu;
249✔
117
  }, []);
118
};
119

120
const listTypeDeviceIdMap = {
187✔
121
  deviceList: getListedDevices,
122
  search: getSearchedDevices
123
};
124
const getDeviceMappingDefaults = () => ({ defaultObject: { auth_sets: [] }, cutOffSize: DEVICE_LIST_MAXIMUM_LENGTH });
1,103✔
125
export const getMappedDevicesList = createSelector(
187✔
126
  [getDevicesById, (state, listType) => listTypeDeviceIdMap[listType](state), getDeviceMappingDefaults],
1,103✔
127
  listItemMapper
128
);
129

130
export const getDeviceCountsByStatus = createSelector([getDevicesByStatus], byStatus =>
187✔
131
  Object.values(DEVICE_STATES).reduce((accu, state) => {
416✔
132
    accu[state] = byStatus[state].total || 0;
1,664✔
133
    return accu;
1,664✔
134
  }, {})
135
);
136

137
export const getDeviceById = createSelector([getDevicesById, (_, deviceId) => deviceId], (devicesById, deviceId = '') => devicesById[deviceId] ?? {});
187✔
138

139
export const getDeviceConfigDeployment = createSelector([getDeviceById, getDeploymentsById], (device, deploymentsById) => {
187✔
140
  const { config = {} } = device;
5✔
141
  const { deployment_id: configDeploymentId } = config;
5✔
142
  const deviceConfigDeployment = deploymentsById[configDeploymentId] || {};
5✔
143
  return { device, deviceConfigDeployment };
5✔
144
});
145

146
export const getSelectedGroupInfo = createSelector(
187✔
147
  [getAcceptedDevices, getGroupsById, getSelectedGroup],
148
  ({ total: acceptedDeviceTotal }, groupsById, selectedGroup) => {
149
    let groupCount = acceptedDeviceTotal;
7✔
150
    let groupFilters = [];
7✔
151
    if (selectedGroup && groupsById[selectedGroup]) {
7✔
152
      groupCount = groupsById[selectedGroup].total;
2✔
153
      groupFilters = groupsById[selectedGroup].filters || [];
2!
154
    }
155
    return { groupCount, selectedGroup, groupFilters };
7✔
156
  }
157
);
158

159
const defaultIdAttribute = Object.freeze({ attribute: 'id', scope: ATTRIBUTE_SCOPES.identity });
187✔
160
export const getIdAttribute = createSelector([getGlobalSettings], ({ id_attribute = { ...defaultIdAttribute } }) => id_attribute);
187✔
161

162
export const getLimitMaxed = createSelector([getAcceptedDevices, getDeviceLimit], ({ total: acceptedDevices = 0 }, deviceLimit) =>
187!
163
  Boolean(deviceLimit && deviceLimit <= acceptedDevices)
7✔
164
);
165

166
export const getFilterAttributes = createSelector(
187✔
167
  [getGlobalSettings, getFilteringAttributes],
168
  ({ previousFilters }, { identityAttributes, inventoryAttributes, systemAttributes, tagAttributes }) => {
169
    const deviceNameAttribute = { key: 'name', value: 'Name', scope: ATTRIBUTE_SCOPES.tags, category: ATTRIBUTE_SCOPES.tags, priority: 1 };
4✔
170
    const deviceIdAttribute = { key: 'id', value: 'Device ID', scope: ATTRIBUTE_SCOPES.identity, category: ATTRIBUTE_SCOPES.identity, priority: 1 };
4✔
171
    const checkInAttribute = { key: 'check_in_time', value: 'Latest activity', scope: ATTRIBUTE_SCOPES.system, category: ATTRIBUTE_SCOPES.system, priority: 4 };
4✔
172
    const updateAttribute = { ...checkInAttribute, key: 'updated_ts', value: 'Last inventory update' };
4✔
173
    const firstRequestAttribute = { key: 'created_ts', value: 'First request', scope: ATTRIBUTE_SCOPES.system, category: ATTRIBUTE_SCOPES.system, priority: 4 };
4✔
174
    const attributes = [
4✔
175
      ...previousFilters.map(item => ({
×
176
        ...item,
177
        value: deviceIdAttribute.key === item.key ? deviceIdAttribute.value : item.key,
×
178
        category: 'recently used',
179
        priority: 0
180
      })),
181
      deviceNameAttribute,
182
      deviceIdAttribute,
183
      ...identityAttributes.map(item => ({ key: item, value: item, scope: ATTRIBUTE_SCOPES.identity, category: ATTRIBUTE_SCOPES.identity, priority: 1 })),
5✔
184
      ...inventoryAttributes.map(item => ({ key: item, value: item, scope: ATTRIBUTE_SCOPES.inventory, category: ATTRIBUTE_SCOPES.inventory, priority: 2 })),
18✔
185
      ...tagAttributes.map(item => ({ key: item, value: item, scope: ATTRIBUTE_SCOPES.tags, category: ATTRIBUTE_SCOPES.tags, priority: 3 })),
×
186
      checkInAttribute,
187
      updateAttribute,
188
      firstRequestAttribute,
189
      ...systemAttributes.map(item => ({ key: item, value: item, scope: ATTRIBUTE_SCOPES.system, category: ATTRIBUTE_SCOPES.system, priority: 4 }))
3✔
190
    ];
191
    return attributeDuplicateFilter(attributes, 'key');
4✔
192
  }
193
);
194

195
// eslint-disable-next-line no-unused-vars
196
export const getGroupsByIdWithoutUngrouped = createSelector([getGroupsById], ({ [UNGROUPED_GROUP.id]: ungrouped, ...groups }) => groups);
187✔
197

198
export const getGroups = createSelector([getGroupsById], groupsById => {
187✔
199
  const groupNames = Object.keys(groupsById).sort();
14✔
200
  const groupedGroups = Object.entries(groupsById)
14✔
201
    .sort((a, b) => a[0].localeCompare(b[0]))
48✔
202
    .reduce(
203
      (accu, [groupname, group]) => {
204
        const name = groupname === UNGROUPED_GROUP.id ? UNGROUPED_GROUP.name : groupname;
40✔
205
        const groupItem = { ...group, groupId: name, name: groupname };
40✔
206
        if (group.filters.length > 0) {
40✔
207
          if (groupname !== UNGROUPED_GROUP.id) {
26✔
208
            accu.dynamic.push(groupItem);
14✔
209
          } else {
210
            accu.ungrouped.push(groupItem);
12✔
211
          }
212
        } else {
213
          accu.static.push(groupItem);
14✔
214
        }
215
        return accu;
40✔
216
      },
217
      { dynamic: [], static: [], ungrouped: [] }
218
    );
219
  return { groupNames, ...groupedGroups };
14✔
220
});
221

222
export const getDeviceTwinIntegrations = createSelector([getExternalIntegrations], integrations =>
187✔
223
  integrations.filter(integration => integration.id && EXTERNAL_PROVIDER[integration.provider]?.deviceTwin)
4✔
224
);
225

226
export const getOfflineThresholdSettings = createSelector([getGlobalSettings], ({ offlineThreshold }) => ({
187✔
227
  interval: offlineThreshold?.interval || DEVICE_ONLINE_CUTOFF.interval,
26✔
228
  intervalUnit: offlineThreshold?.intervalUnit || DEVICE_ONLINE_CUTOFF.intervalName
26✔
229
}));
230

231
export const getOnboardingState = createSelector([getOnboarding, getShowHelptips], ({ complete, progress, showTips, ...remainder }, showHelptips) => ({
187✔
232
  ...remainder,
233
  complete,
234
  progress,
235
  showHelptips,
236
  showTips
237
}));
238

239
export const getDocsVersion = createSelector([getAppDocsVersion, getFeatures], (appDocsVersion, { isHosted }) => {
187✔
240
  // if hosted, use latest docs version
241
  const docsVersion = appDocsVersion ? `${appDocsVersion}/` : 'development/';
38!
242
  return isHosted ? '' : docsVersion;
38✔
243
});
244

245
export const getIsEnterprise = createSelector(
187✔
246
  [getOrganization, getFeatures],
247
  ({ plan = PLANS.os.value }, { isEnterprise, isHosted }) => isEnterprise || (isHosted && plan === PLANS.enterprise.value)
71✔
248
);
249

250
export const getAttributesList = createSelector(
187✔
251
  [getFilteringAttributes, getFilteringAttributesFromConfig],
252
  ({ identityAttributes = [], inventoryAttributes = [] }, { identity = [], inventory = [] }) =>
42!
253
    [...identityAttributes, ...inventoryAttributes, ...identity, ...inventory].filter(duplicateFilter)
21✔
254
);
255

256
export const getRolesList = createSelector([getRolesById], rolesById => Object.entries(rolesById).map(([id, role]) => ({ id, ...role })));
187✔
257

258
export const getUserRoles = createSelector(
187✔
259
  [getCurrentUser, getRolesById, getIsEnterprise, getFeatures, getOrganization],
260
  (currentUser, rolesById, isEnterprise, { isHosted, hasMultitenancy }, { plan = PLANS.os.value }) => {
9✔
261
    const isAdmin = currentUser.roles?.length
66✔
262
      ? currentUser.roles.some(role => role === rolesByName.admin)
59✔
263
      : !(hasMultitenancy || isEnterprise || (isHosted && plan !== PLANS.os.value));
15!
264
    const uiPermissions = isAdmin
66✔
265
      ? mapUserRolesToUiPermissions([rolesByName.admin], rolesById)
266
      : mapUserRolesToUiPermissions(currentUser.roles || [], rolesById);
6✔
267
    return { isAdmin, uiPermissions };
66✔
268
  }
269
);
270

271
const hasPermission = (thing, permission) => Object.values(thing).some(permissions => permissions.includes(permission));
304✔
272

273
export const getUserCapabilities = createSelector([getUserRoles], ({ uiPermissions }) => {
187✔
274
  const canManageReleases = hasPermission(uiPermissions.releases, uiPermissionsById.manage.value);
59✔
275
  const canReadReleases = canManageReleases || hasPermission(uiPermissions.releases, uiPermissionsById.read.value);
59✔
276
  const canUploadReleases = canManageReleases || hasPermission(uiPermissions.releases, uiPermissionsById.upload.value);
59✔
277

278
  const canAuditlog = uiPermissions.auditlog.includes(uiPermissionsById.read.value);
59✔
279

280
  const canReadUsers = uiPermissions.userManagement.includes(uiPermissionsById.read.value);
59✔
281
  const canManageUsers = uiPermissions.userManagement.includes(uiPermissionsById.manage.value);
59✔
282

283
  const canReadDevices = hasPermission(uiPermissions.groups, uiPermissionsById.read.value);
59✔
284
  const canWriteDevices = Object.values(uiPermissions.groups).some(
59✔
285
    groupPermissions => groupPermissions.includes(uiPermissionsById.read.value) && groupPermissions.length > 1
56✔
286
  );
287
  const canTroubleshoot = hasPermission(uiPermissions.groups, uiPermissionsById.connect.value);
59✔
288
  const canManageDevices = hasPermission(uiPermissions.groups, uiPermissionsById.manage.value);
59✔
289
  const canConfigure = hasPermission(uiPermissions.groups, uiPermissionsById.configure.value);
59✔
290

291
  const canDeploy = uiPermissions.deployments.includes(uiPermissionsById.deploy.value) || hasPermission(uiPermissions.groups, uiPermissionsById.deploy.value);
59✔
292
  const canReadDeployments = uiPermissions.deployments.includes(uiPermissionsById.read.value);
59✔
293

294
  return {
59✔
295
    canAuditlog,
296
    canConfigure,
297
    canDeploy,
298
    canManageDevices,
299
    canManageReleases,
300
    canManageUsers,
301
    canReadDeployments,
302
    canReadDevices,
303
    canReadReleases,
304
    canReadUsers,
305
    canTroubleshoot,
306
    canUploadReleases,
307
    canWriteDevices,
308
    groupsPermissions: uiPermissions.groups,
309
    releasesPermissions: uiPermissions.releases
310
  };
311
});
312

313
export const getTenantCapabilities = createSelector(
187✔
314
  [getFeatures, getOrganization, getIsEnterprise],
315
  (
316
    {
317
      hasAddons,
318
      hasAuditlogs: isAuditlogEnabled,
319
      hasDeviceConfig: isDeviceConfigEnabled,
320
      hasDeviceConnect: isDeviceConnectEnabled,
321
      hasMonitor: isMonitorEnabled,
322
      isHosted
323
    },
324
    { addons = [], plan },
6✔
325
    isEnterprise
326
  ) => {
327
    const canDelta = isEnterprise || plan === PLANS.professional.value;
39✔
328
    const hasAuditlogs = isAuditlogEnabled && (!isHosted || isEnterprise || plan === PLANS.professional.value);
39!
329
    const hasDeviceConfig = hasAddons || (isDeviceConfigEnabled && (!isHosted || addons.some(addon => addon.name === 'configure' && Boolean(addon.enabled))));
39!
330
    const hasDeviceConnect =
331
      hasAddons || (isDeviceConnectEnabled && (!isHosted || addons.some(addon => addon.name === 'troubleshoot' && Boolean(addon.enabled))));
39!
332
    const hasMonitor = hasAddons || (isMonitorEnabled && (!isHosted || addons.some(addon => addon.name === 'monitor' && Boolean(addon.enabled))));
39!
333
    return {
39✔
334
      canDelta,
335
      canRetry: canDelta,
336
      canSchedule: canDelta,
337
      hasAuditlogs,
338
      hasDeviceConfig,
339
      hasDeviceConnect,
340
      hasFullFiltering: canDelta,
341
      hasMonitor,
342
      isEnterprise
343
    };
344
  }
345
);
346

347
export const getAvailableIssueOptionsByType = createSelector(
187✔
348
  [getFeatures, getTenantCapabilities, getIssueCountsByType],
349
  ({ hasReporting }, { hasFullFiltering, hasMonitor }, issueCounts) =>
350
    Object.values(DEVICE_ISSUE_OPTIONS).reduce((accu, { isCategory, key, needsFullFiltering, needsMonitor, needsReporting, title }) => {
18✔
351
      if (isCategory || (needsReporting && !hasReporting) || (needsFullFiltering && !hasFullFiltering) || (needsMonitor && !hasMonitor)) {
108✔
352
        return accu;
102✔
353
      }
354
      accu[key] = { count: issueCounts[key].filtered, key, title };
6✔
355
      return accu;
6✔
356
    }, {})
357
);
358

359
export const getDeviceTypes = createSelector([getAcceptedDevices, getDevicesById], ({ deviceIds = [] }, devicesById) =>
187!
360
  Object.keys(
1✔
361
    deviceIds.slice(0, 200).reduce((accu, item) => {
362
      const { device_type: deviceTypes = [] } = devicesById[item] ? devicesById[item].attributes : {};
2!
363
      accu = deviceTypes.reduce((deviceTypeAccu, deviceType) => {
2✔
364
        if (deviceType.length > 1) {
2!
365
          deviceTypeAccu[deviceType] = deviceTypeAccu[deviceType] ? deviceTypeAccu[deviceType] + 1 : 1;
2!
366
        }
367
        return deviceTypeAccu;
2✔
368
      }, accu);
369
      return accu;
2✔
370
    }, {})
371
  )
372
);
373

374
export const getGroupNames = createSelector([getGroupsById, getUserRoles, (_, options = {}) => options], (groupsById, { uiPermissions }, { staticOnly }) => {
1,483✔
375
  // eslint-disable-next-line no-unused-vars
376
  const { [UNGROUPED_GROUP.id]: ungrouped, ...groups } = groupsById;
1,483✔
377
  if (staticOnly) {
1,483!
378
    return Object.keys(uiPermissions.groups).sort();
×
379
  }
380
  return Object.keys(
1,483✔
381
    Object.entries(groups).reduce((accu, [groupName, group]) => {
382
      if (group.filterId || uiPermissions.groups[ALL_DEVICES]) {
2,918✔
383
        accu[groupName] = group;
1,990✔
384
      }
385
      return accu;
2,918✔
386
    }, uiPermissions.groups)
387
  ).sort();
388
});
389

390
const getReleaseMappingDefaults = () => ({});
187✔
391
export const getReleasesList = createSelector([getReleasesById, getListedReleases, getReleaseMappingDefaults], listItemMapper);
187✔
392

393
export const getReleaseTagsById = createSelector([getReleaseTags], releaseTags => releaseTags.reduce((accu, key) => ({ ...accu, [key]: key }), {}));
187✔
394

395
const relevantDeploymentStates = [DEPLOYMENT_STATES.pending, DEPLOYMENT_STATES.inprogress, DEPLOYMENT_STATES.finished];
187✔
396
export const DEPLOYMENT_CUTOFF = 3;
187✔
397
export const getRecentDeployments = createSelector([getDeploymentsById, getDeploymentsByStatus], (deploymentsById, deploymentsByStatus) =>
187✔
398
  Object.entries(deploymentsByStatus).reduce(
415✔
399
    (accu, [state, byStatus]) => {
400
      if (!relevantDeploymentStates.includes(state) || !byStatus.deploymentIds.length) {
1,660✔
401
        return accu;
439✔
402
      }
403
      accu[state] = byStatus.deploymentIds
1,221✔
404
        .reduce((accu, id) => {
405
          if (deploymentsById[id]) {
2,376✔
406
            accu.push(deploymentsById[id]);
2,370✔
407
          }
408
          return accu;
2,376✔
409
        }, [])
410
        .slice(0, DEPLOYMENT_CUTOFF);
411
      accu.total += byStatus.total;
1,221✔
412
      return accu;
1,221✔
413
    },
414
    { total: 0 }
415
  )
416
);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc