qa-shadow-report
Version:
npm package that prints formatted test reports into a google sheet or csv file
396 lines (343 loc) • 13.1 kB
JavaScript
import {
getDayIndex,
getFormattedMonth,
getPreviousMonthsYear,
getTodaysFormattedDate,
} from '../../sharedMethods/dateFormatting.js';
import { getTopLevelSpreadsheetData } from '../googleSheetIntegration/getSheetData.js';
import { dataObjects } from '../../index.js';
import { createSummaryTitle } from './createTabNames.js';
import { createWeeklySummaryTitle } from '../../sharedMethods/summaryHandler.js';
import {
FOOTER_ROW,
HEADER_INDICATORS,
WEEK_START,
WEEKLY_SUMMARY_ENABLED,
} from '../../../constants.js';
/**
* Find a sheet data object in the array that matches a specific title.
* @param {Array} sheetValuesArray - Array of sheet data objects.
* @param {string} sheetTitle - The title of the sheet to find.
* @returns {Object|undefined} The sheet data object or undefined if not found.
* @throws {TypeError} - If the input types are invalid.
*/
export const findTabTitleDataInArray = (sheetValuesArray, sheetTitle) => {
if (!Array.isArray(sheetValuesArray)) {
throw new TypeError('Invalid sheetValuesArray: Expected an array.');
}
if (typeof sheetTitle !== 'string') {
throw new TypeError('Invalid sheetTitle: Expected a string.');
}
return sheetValuesArray.find((sheet) => {
if (typeof sheet.config.url !== 'string') {
throw new TypeError('Invalid sheet config: Expected url to be a string.');
}
const url = decodeURIComponent(sheet.config.url);
return url.includes(`/${sheetTitle}`);
});
};
/**
* Fetches existing tab titles in range based on the given criteria.
* @param {string} when - Time frame filter criteria.
* @returns {Promise<Array>} An array of titles that match the given criteria.
* @throws {Error} If fetching or processing data fails.
*/
export const getExistingTabTitlesInRange = async (when = '') => {
if (typeof when !== 'string') {
throw new TypeError('Invalid input: "when" must be a string.');
}
const currentMonth = getFormattedMonth();
const lastMonth = getFormattedMonth('lastMonth').toLowerCase();
const previousMonthYear = getPreviousMonthsYear(currentMonth);
try {
const metaData =
Object.keys(dataObjects.topLevelSpreadsheetData).length === 0
? await getTopLevelSpreadsheetData()
: dataObjects.topLevelSpreadsheetData;
if (!metaData || !metaData.data || !Array.isArray(metaData.data.sheets)) {
throw new Error('Invalid spreadsheet metadata format.');
}
const titles = metaData.data.sheets.map((sheet) => sheet.properties.title);
if (!Array.isArray(titles)) {
throw new Error('Invalid titles format: Expected an array of strings.');
}
return when !== 'lastMonth'
? titles
: titles.filter(
(title) =>
title.toLowerCase().includes(lastMonth) &&
title.includes(previousMonthYear) &&
/^([A-Z][a-z]{2} \d{1,2}, \d{4})$/.test(title)
);
} catch (error) {
console.error('Error fetching sheet titles in range:', error);
throw new Error('Unable to fetch sheet titles in range.');
}
};
/**
* Fetches existing tab titles within the current weekly range.
* @returns {Promise<Array>} An array of tab titles that fall within the weekly range.
* @throws {Error} If fetching or processing data fails.
*/
export const getExistingTabTitlesInWeeklyRange = async () => {
try {
const now = new Date();
const startDate = new Date(now);
startDate.setDate(
now.getDate() - ((now.getDay() + 7 - getDayIndex(WEEK_START())) % 7)
);
startDate.setHours(0, 0, 0, 0);
const endDate = new Date(startDate);
endDate.setDate(startDate.getDate() + 6);
endDate.setHours(23, 59, 59, 999);
const metaData =
Object.keys(dataObjects.topLevelSpreadsheetData).length === 0
? await getTopLevelSpreadsheetData()
: dataObjects.topLevelSpreadsheetData;
if (!metaData || !metaData.data || !Array.isArray(metaData.data.sheets)) {
throw new Error('Invalid spreadsheet metadata format.');
}
const titles = metaData.data.sheets.map((sheet) => sheet.properties.title);
if (!Array.isArray(titles)) {
throw new Error('Invalid titles format: Expected an array of strings.');
}
return titles.filter((title) => {
const tabDate = new Date(title);
return tabDate >= startDate && tabDate <= endDate;
});
} catch (error) {
console.error('Error fetching sheet titles in weekly range:', error);
throw new Error('Unable to fetch sheet titles in weekly range.');
}
};
/**
* Gets the ID of the tab by its title.
* @param {string} tabTitle - The title of the tab.
* @returns {Promise<number|null>} The ID of the tab or null if not found.
* @throws {Error} If fetching or processing data fails.
*/
export const getTabIdFromTitle = async (tabTitle) => {
if (typeof tabTitle !== 'string' || tabTitle.trim() === '') {
throw new Error('Invalid tabTitle: Expected a non-empty string.');
}
let sheetList;
if (tabTitle.includes(createSummaryTitle())) {
sheetList = dataObjects.summaryTabData;
} else if (tabTitle.includes(getTodaysFormattedDate())) {
sheetList = dataObjects.todaysReportData;
} else {
sheetList = dataObjects.topLevelSpreadsheetData;
}
if (!sheetList || Object.keys(sheetList).length === 0) {
console.error('Sheet list data is not available');
return null;
}
try {
if (
tabTitle.includes(createSummaryTitle()) ||
tabTitle.includes(getTodaysFormattedDate())
) {
return sheetList.data.replies[0].addSheet.properties.sheetId;
}
const sheet = sheetList.data.sheets.find(
(sheet) => sheet.properties.title === tabTitle
);
return sheet ? sheet.properties.sheetId : null;
} catch (error) {
console.error('Error fetching tab ID from title:', error);
throw new Error('Unable to fetch tab ID from title.');
}
};
/**
* Gets the ID of the tab by its title.
* @param {string} tabTitle - The title of the tab.
* @returns {Promise<number|null>} The ID of the tab or null if not found.
* @throws {Error} If fetching or processing data fails.
*/
export const getTabIdFromWeeklyTitle = async (tabTitle) => {
if (typeof tabTitle !== 'string' || tabTitle.trim() === '') {
throw new Error('Invalid tabTitle: Expected a non-empty string.');
}
let sheetList;
if (tabTitle.includes(createWeeklySummaryTitle())) {
sheetList = dataObjects.weeklySummaryTabData;
} else if (
tabTitle.includes(getTodaysFormattedDate()) &&
!WEEKLY_SUMMARY_ENABLED()
) {
sheetList = dataObjects.todaysReportData;
} else {
sheetList = dataObjects.topLevelSpreadsheetData;
}
if (!sheetList || Object.keys(sheetList).length === 0) {
console.error('Sheet list data is not available');
return null;
}
try {
if (
tabTitle.includes(createWeeklySummaryTitle()) ||
(tabTitle.includes(getTodaysFormattedDate()) && !WEEKLY_SUMMARY_ENABLED())
) {
return sheetList.data.replies[0].addSheet.properties.sheetId;
}
const sheet = sheetList.data.sheets.find(
(sheet) => sheet.properties.title === tabTitle
);
return sheet ? sheet.properties.sheetId : null;
} catch (error) {
console.error('Error fetching tab ID from title:', error);
throw new Error('Unable to fetch tab ID from title.');
}
};
/**
* Finds the index of the column whose header matches any of the default metrics.
* @param {string} sheetId - The ID of the sheet/tab to look into.
* @param {Array<string>} defaultHeaderMetrics - An array of metric names to match against.
* @returns {Promise<number|null>} The index of the matching column or null if no match is found.
* @throws {Error} If fetching or processing data fails.
*/
export const findMatchingColumnByWeeklyTabId = async (
sheetId,
defaultHeaderMetrics
) => {
if (typeof sheetId !== 'string' || sheetId.trim() === '') {
throw new Error('Invalid sheetId: Expected a non-empty string.');
}
if (
!Array.isArray(defaultHeaderMetrics) ||
defaultHeaderMetrics.length === 0
) {
throw new Error(
'Invalid defaultHeaderMetrics: Expected a non-empty array.'
);
}
try {
const metaData = dataObjects.lastWeekSheetValues[0].find((item) =>
item.data.range.startsWith(`'${sheetId}'`)
);
if (!metaData || !metaData.data.values || !metaData.data.values[0]) {
throw new Error(
`Sheet data not found or invalid for sheetId: ${sheetId}`
);
}
const headers = metaData.data.values[0];
const matchingIndex = headers.findIndex((header) =>
defaultHeaderMetrics.some((metric) => header.trim().includes(metric))
);
return matchingIndex !== -1 ? matchingIndex : null;
} catch (error) {
console.error('Error finding matching column by tab ID:', error);
throw new Error('Unable to find matching column by tab ID.');
}
};
/**
* Finds the index of the column whose header matches any of the default metrics.
* @param {string} sheetId - The ID of the sheet/tab to look into.
* @param {Array<string>} defaultHeaderMetrics - An array of metric names to match against.
* @returns {Promise<number|null>} The index of the matching column or null if no match is found.
* @throws {Error} If fetching or processing data fails.
*/
export const findMatchingColumnByTabId = async (
sheetId,
defaultHeaderMetrics
) => {
if (typeof sheetId !== 'string' || sheetId.trim() === '') {
throw new Error('Invalid sheetId: Expected a non-empty string.');
}
if (
!Array.isArray(defaultHeaderMetrics) ||
defaultHeaderMetrics.length === 0
) {
throw new Error(
'Invalid defaultHeaderMetrics: Expected a non-empty array.'
);
}
try {
const metaData = dataObjects.lastMonthSheetValues[0].find((item) =>
item.data.range.startsWith(`'${sheetId}'`)
);
if (!metaData || !metaData.data.values || !metaData.data.values[0]) {
throw new Error(
`Sheet data not found or invalid for sheetId: ${sheetId}`
);
}
const headers = metaData.data.values[0];
const matchingIndex = headers.findIndex((header) =>
defaultHeaderMetrics.some((metric) => header.trim().includes(metric))
);
return matchingIndex !== -1 ? matchingIndex : null;
} catch (error) {
console.error('Error finding matching column by tab ID:', error);
throw new Error('Unable to find matching column by tab ID.');
}
};
/**
* Retrieves header and footer data of a tab by its title.
* @param {string} sheetTitle - The title of the tab.
* @returns {Promise<Object|null>} An object containing header row, footer row, and header values, or null if not found.
* @throws {Error} Throws an error if data retrieval fails.
*/
export const getHeaderAndFooterDataByTabTitle = async (sheetTitle) => {
if (typeof sheetTitle !== 'string' || sheetTitle.trim() === '') {
throw new Error('Invalid sheetTitle: Expected a non-empty string.');
}
try {
let headerRow = 0;
let footerRow = 0;
let headerValues = [];
const metaData = dataObjects.lastMonthSheetValues[0].find((item) =>
item.data.range.startsWith(`'${sheetTitle}'`)
);
if (!metaData) {
console.error(`No data found for sheet title: ${sheetTitle}`);
return null;
}
const rows = metaData.data.values;
rows.forEach((row, index) => {
if (row.includes(FOOTER_ROW)) footerRow = index + 1;
if (HEADER_INDICATORS.every((indicator) => row.includes(indicator))) {
headerRow = index + 1;
headerValues = row;
}
});
return { headerRow, footerRow, headerValues };
} catch (error) {
console.error('Error retrieving header and footer data:', error);
throw new Error('Unable to retrieve header and footer data.');
}
};
/**
* Retrieves header and footer data of a tab by its title.
* @param {string} sheetTitle - The title of the tab.
* @returns {Promise<Object|null>} An object containing header row, footer row, and header values, or null if not found.
* @throws {Error} Throws an error if data retrieval fails.
*/
export const getHeaderAndFooterDataByWeeklyTabTitle = async (sheetTitle) => {
if (typeof sheetTitle !== 'string' || sheetTitle.trim() === '') {
throw new Error('Invalid sheetTitle: Expected a non-empty string.');
}
try {
let headerRow = 0;
let footerRow = 0;
let headerValues = [];
const metaData = dataObjects.lastWeekSheetValues[0].find((item) =>
item.data.range.startsWith(`'${sheetTitle}'`)
);
if (!metaData) {
console.error(`No data found for sheet title: ${sheetTitle}`);
return null;
}
const rows = metaData.data.values;
rows.forEach((row, index) => {
if (row.includes(FOOTER_ROW)) footerRow = index + 1;
if (HEADER_INDICATORS.every((indicator) => row.includes(indicator))) {
headerRow = index + 1;
headerValues = row;
}
});
return { headerRow, footerRow, headerValues };
} catch (error) {
console.error('Error retrieving header and footer data:', error);
throw new Error('Unable to retrieve header and footer data.');
}
};