_getEntityGroupItems(details) {
    const {items, headings, sortedBy} = details;
    // Exclude entity-grouped audits and results without entity classification.
    // Eg. Third-party Summary comes entity-grouped.
    if (!items.length || details.isEntityGrouped || !items.some(item => item.entity)) {
      return [];
    }

    const skippedColumns = new Set(details.skipSumming || []);
    /** @type {string[]} */
    const summableColumns = [];
    for (const heading of headings) {
      if (!heading.key || skippedColumns.has(heading.key)) continue;
      if (SUMMABLE_VALUETYPES.includes(heading.valueType)) {
        summableColumns.push(heading.key);
      }
    }

    // Grab the first column's key to group by entity
    const firstColumnKey = headings[0].key;
    if (!firstColumnKey) return [];

    /** @type {Map<string | undefined, TableItem>} */
    const byEntity = new Map();
    for (const item of items) {
      const entityName = typeof item.entity === 'string' ? item.entity : undefined;
      const groupedItem = byEntity.get(entityName) || {
        [firstColumnKey]: entityName || Globals.strings.unattributable,
        entity: entityName,
      };
      for (const key of summableColumns) {
        groupedItem[key] = Number(groupedItem[key] || 0) + Number(item[key] || 0);
      }
      byEntity.set(entityName, groupedItem);
    }

    const result = [...byEntity.values()];
    if (sortedBy) {
      result.sort(ReportUtils.getTableItemSortComparator(sortedBy));
    }
    return result;
  }
Returns an array of entity-grouped TableItems to use as the top-level rows in
an grouped table. Each table item returned represents a unique entity, with every
applicable key that can be grouped as a property. Optionally, supported columns are
summed by entity, and sorted by specified keys.
@param {TableLike} details
@return {TableItem[]}