Created
May 21, 2021 13:00
-
-
Save NikhilP97/cdbb64e62803c550df4a2606334ce1a8 to your computer and use it in GitHub Desktop.
Downloads the table details of app store testflight as a CSV file on your disk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This JS script downloads user details from test flight into a CSV File. | |
* Run each step at a time and not all together. | |
* Please read instructions for step 1. | |
*/ | |
// ---------------Step 0 starts--------------- | |
/** | |
* CONFIGURATION | |
*/ | |
const APP_CONFIGURATION = { | |
/** | |
* To add additional columns, inspect the DOM and check how to identify the cell uniquely. | |
* In the case of email, there is an <a> tag inside which the text is present, hence we use that as the html query. | |
* For status and name, there is only one <span> tag inside which the text is present. | |
* For session, crashes, feedback, the text is inside a <div> tag and hence we cannot use it in the query. | |
* Hence we use the class 'tester-metrics-cell-text' to identify the enclosing text tag. | |
* This code depends on the current DOM structure to work. | |
* In the future, if the column positions are shifted, if the class names are changed or if the html tags are changed, | |
* make the changes to the below configuration accordingly. | |
*/ | |
columnDetails: [ | |
{ name: 'email', columnNo: 0, htmlQuery: 'a' }, | |
{ name: 'status', columnNo: 2, htmlQuery: 'span' }, | |
// { name: 'name', columnNo: 1, htmlQuery: 'span' }, | |
// { name: 'sessions', columnNo: 3, htmlQuery: '.tester-metrics-cell-text' }, | |
// { name: 'crashes', columnNo: 4, htmlQuery: '.tester-metrics-cell-text' }, | |
// { name: 'feedback', columnNo: 5, htmlQuery: '.tester-metrics-cell-text' }, | |
], | |
csvFileName: 'App-ios-test-flight-details.csv', | |
}; | |
/** | |
* GLOBAL VARIABLES | |
*/ | |
const APP_GLOBAL_VARS = { | |
userData: [], | |
emailSet: new Set(), | |
csvStr: '', | |
}; | |
console.log('Successfully initialized variables!!!'); | |
// ---------------Step 0 ends--------------- | |
// ---------------Step 1 starts--------------- | |
/** | |
* Step 1 - Find and store data in the global variables. | |
* IMPORTANT: | |
* | |
* Run this step multiple times until the length of the data is equal to the table length. | |
* This needs to be done because react virtualization library is being used which does not | |
* render all the information on the DOM at one time. | |
* So keep scrolling and run this script until all the data is captured. | |
* | |
*/ | |
((APP_GLOBAL_VARS, APP_CONFIGURATION) => { | |
const { userData, emailSet } = APP_GLOBAL_VARS; | |
// details about the columns in the table | |
const { columnDetails } = APP_CONFIGURATION; | |
const emailColumn = columnDetails.find(column => column.name === 'email'); | |
// iterate through each row and store details | |
document.querySelector('.ReactVirtualized__Grid__innerScrollContainer').childNodes.forEach((rowNode) => { | |
const email = rowNode.childNodes[emailColumn.columnNo].querySelector(emailColumn.htmlQuery).textContent; | |
// if email already exists, continue | |
if (emailSet.has(email)) return; | |
// else store the email in the set & user data array | |
emailSet.add(email); | |
const newUserDetails = {}; | |
for (const column of columnDetails) { | |
const { name, columnNo, htmlQuery } = column; | |
const colNode = rowNode.childNodes[columnNo].querySelector(htmlQuery); | |
const textValue = colNode ? colNode.textContent : ''; | |
newUserDetails[name] = textValue; | |
} | |
userData.push(newUserDetails); | |
}); | |
console.log('No of users captured: ', userData.length); | |
})(APP_GLOBAL_VARS, APP_CONFIGURATION); | |
// ---------------Step 1 ends--------------- | |
// ---------------Step 2 starts--------------- | |
/** | |
* Step 2 - Transform array of data to comma separated values. | |
*/ | |
APP_GLOBAL_VARS.csvStr = ((APP_GLOBAL_VARS) => { | |
const { userData } = APP_GLOBAL_VARS; | |
if (userData.length === 0) throw new Error('No data captured, run step 1 again'); | |
// CSV header row | |
const HEADER = Object.keys(userData[0]).map(keyName => keyName.toUpperCase()).join(','); | |
// extra space to leave a line after header | |
const CSV_HEADER = `${HEADER}\n\n`; | |
// CSV values | |
const commaSeparatedValues = userData.map(details => { | |
const objectArray = Object.values(details); | |
return objectArray.join(','); | |
}); | |
const lineSeparatedStr = commaSeparatedValues.join('\n'); | |
console.log('Successfully created CSV string!!!'); | |
return CSV_HEADER + lineSeparatedStr; | |
})(APP_GLOBAL_VARS); | |
// ---------------Step 2 ends--------------- | |
// ---------------Step 3 starts--------------- | |
/** | |
* Step 3 - Download CSV file to disk | |
*/ | |
((APP_GLOBAL_VARS, APP_CONFIGURATION) => { | |
// load variables | |
const { csvStr } = APP_GLOBAL_VARS; | |
const { csvFileName } = APP_CONFIGURATION; | |
// transform to csv encoded string | |
const encodedUri = encodeURI(`data:text/csv;charset=utf-8,${csvStr}`); | |
var link = document.createElement('a'); | |
link.setAttribute('href', encodedUri); | |
link.setAttribute('download', csvFileName); | |
document.body.appendChild(link); | |
// This will download the csv file | |
link.click(); | |
console.log('Successfully downloaded CSV file!!!'); | |
})(APP_GLOBAL_VARS, APP_CONFIGURATION); | |
// ---------------Step 3 ends--------------- |
this script is amazing! thank you!!!!! apple has put the date of install in the second row of the status column. can you suggest a code snippet to capture that as well? would be a lifesaver. nick
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Any tips to get past this error in Step 1