diff --git a/.github/actions/awaitStagingDeploys/index.js b/.github/actions/awaitStagingDeploys/index.js
index 314a4d03295b..c6ece6eee302 100644
--- a/.github/actions/awaitStagingDeploys/index.js
+++ b/.github/actions/awaitStagingDeploys/index.js
@@ -226,6 +226,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -295,6 +297,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -304,6 +308,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -376,6 +382,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/checkDeployBlockers/checkDeployBlockers.js b/.github/actions/checkDeployBlockers/checkDeployBlockers.js
index 89c4843cc9d3..db156651f9fe 100644
--- a/.github/actions/checkDeployBlockers/checkDeployBlockers.js
+++ b/.github/actions/checkDeployBlockers/checkDeployBlockers.js
@@ -16,7 +16,7 @@ const run = function () {
console.log('Checking for unverified PRs or unresolved deploy blockers', data);
// Check the issue description to see if there are any unfinished/un-QAed items in the checklist.
- const uncheckedBoxRegex = new RegExp(`-\\s\\[\\s]\\s(?:QA|${GithubUtils.ISSUE_OR_PULL_REQUEST_REGEX.source})`);
+ const uncheckedBoxRegex = /-\s\[\s]\s(?!Accessibility)/;
if (uncheckedBoxRegex.test(data.body)) {
console.log('An unverified PR or unresolved deploy blocker was found.');
core.setOutput('HAS_DEPLOY_BLOCKERS', true);
diff --git a/.github/actions/checkDeployBlockers/index.js b/.github/actions/checkDeployBlockers/index.js
index 51e878382f01..179dc8b4a078 100644
--- a/.github/actions/checkDeployBlockers/index.js
+++ b/.github/actions/checkDeployBlockers/index.js
@@ -26,7 +26,7 @@ const run = function () {
console.log('Checking for unverified PRs or unresolved deploy blockers', data);
// Check the issue description to see if there are any unfinished/un-QAed items in the checklist.
- const uncheckedBoxRegex = new RegExp(`-\\s\\[\\s]\\s(?:QA|${GithubUtils.ISSUE_OR_PULL_REQUEST_REGEX.source})`);
+ const uncheckedBoxRegex = /-\s\[\s]\s(?!Accessibility)/;
if (uncheckedBoxRegex.test(data.body)) {
console.log('An unverified PR or unresolved deploy blocker was found.');
core.setOutput('HAS_DEPLOY_BLOCKERS', true);
@@ -196,6 +196,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -265,6 +267,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -274,6 +278,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -346,6 +352,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js b/.github/actions/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js
index 56535f03b29c..43992307b912 100644
--- a/.github/actions/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js
+++ b/.github/actions/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js
@@ -85,6 +85,7 @@ const run = function () {
// If we aren't sent a tag, then use the existing tag
const tag = newVersion || currentStagingDeployCashData.tag;
+ const didVersionChange = newVersion ? newVersion !== currentStagingDeployCashData.tag : false;
// Find the list of PRs merged between the last StagingDeployCash and the new version
const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, tag);
@@ -128,6 +129,8 @@ const run = function () {
_.pluck(_.where(PRList, {isAccessible: true}), 'url'),
_.pluck(deployBlockers, 'url'),
_.pluck(_.where(deployBlockers, {isResolved: true}), 'url'),
+ didVersionChange ? false : currentStagingDeployCashData.isTimingDashboardChecked,
+ didVersionChange ? false : currentStagingDeployCashData.isFirebaseChecked,
);
})
.then((body) => {
diff --git a/.github/actions/createOrUpdateStagingDeploy/index.js b/.github/actions/createOrUpdateStagingDeploy/index.js
index 58ecf86f7e9d..43fda09543d8 100644
--- a/.github/actions/createOrUpdateStagingDeploy/index.js
+++ b/.github/actions/createOrUpdateStagingDeploy/index.js
@@ -95,6 +95,7 @@ const run = function () {
// If we aren't sent a tag, then use the existing tag
const tag = newVersion || currentStagingDeployCashData.tag;
+ const didVersionChange = newVersion ? newVersion !== currentStagingDeployCashData.tag : false;
// Find the list of PRs merged between the last StagingDeployCash and the new version
const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, tag);
@@ -138,6 +139,8 @@ const run = function () {
_.pluck(_.where(PRList, {isAccessible: true}), 'url'),
_.pluck(deployBlockers, 'url'),
_.pluck(_.where(deployBlockers, {isResolved: true}), 'url'),
+ didVersionChange ? false : currentStagingDeployCashData.isTimingDashboardChecked,
+ didVersionChange ? false : currentStagingDeployCashData.isFirebaseChecked,
);
})
.then((body) => {
@@ -386,6 +389,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -455,6 +460,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -464,6 +471,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -536,6 +545,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/getPullRequestDetails/index.js b/.github/actions/getPullRequestDetails/index.js
index 0e8fcb725620..8ef5d2f04ebf 100644
--- a/.github/actions/getPullRequestDetails/index.js
+++ b/.github/actions/getPullRequestDetails/index.js
@@ -267,6 +267,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -336,6 +338,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -345,6 +349,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -417,6 +423,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/getReleaseBody/index.js b/.github/actions/getReleaseBody/index.js
index c8c7a0d153d3..a72d730563ff 100644
--- a/.github/actions/getReleaseBody/index.js
+++ b/.github/actions/getReleaseBody/index.js
@@ -185,6 +185,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -254,6 +256,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -263,6 +267,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -335,6 +341,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/isPullRequestMergeable/index.js b/.github/actions/isPullRequestMergeable/index.js
index 3e98b961c4f7..b03366d0c9dd 100644
--- a/.github/actions/isPullRequestMergeable/index.js
+++ b/.github/actions/isPullRequestMergeable/index.js
@@ -186,6 +186,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -255,6 +257,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -264,6 +268,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -336,6 +342,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/isStagingDeployLocked/index.js b/.github/actions/isStagingDeployLocked/index.js
index 4ef5e5aa3c6e..a0e47bbe0662 100644
--- a/.github/actions/isStagingDeployLocked/index.js
+++ b/.github/actions/isStagingDeployLocked/index.js
@@ -149,6 +149,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -218,6 +220,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -227,6 +231,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -299,6 +305,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/markPullRequestsAsDeployed/index.js b/.github/actions/markPullRequestsAsDeployed/index.js
index 1ad199a5f4cf..4f881fae56c4 100644
--- a/.github/actions/markPullRequestsAsDeployed/index.js
+++ b/.github/actions/markPullRequestsAsDeployed/index.js
@@ -318,6 +318,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -387,6 +389,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -396,6 +400,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -468,6 +474,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/reopenIssueWithComment/index.js b/.github/actions/reopenIssueWithComment/index.js
index 42d09f1c766e..7c0219f6e2b3 100644
--- a/.github/actions/reopenIssueWithComment/index.js
+++ b/.github/actions/reopenIssueWithComment/index.js
@@ -160,6 +160,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -229,6 +231,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -238,6 +242,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -310,6 +316,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/triggerWorkflowAndWait/index.js b/.github/actions/triggerWorkflowAndWait/index.js
index 86a6f5039af5..a04038faa3c0 100644
--- a/.github/actions/triggerWorkflowAndWait/index.js
+++ b/.github/actions/triggerWorkflowAndWait/index.js
@@ -329,6 +329,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -398,6 +400,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -407,6 +411,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -479,6 +485,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/actions/verifySignedCommits/index.js b/.github/actions/verifySignedCommits/index.js
index 8060ed706572..406598e3db99 100644
--- a/.github/actions/verifySignedCommits/index.js
+++ b/.github/actions/verifySignedCommits/index.js
@@ -149,6 +149,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -218,6 +220,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -227,6 +231,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -299,6 +305,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/libs/GithubUtils.js b/.github/libs/GithubUtils.js
index be5fe177c54a..220407dd1f2f 100644
--- a/.github/libs/GithubUtils.js
+++ b/.github/libs/GithubUtils.js
@@ -109,6 +109,8 @@ class GithubUtils {
labels: issue.labels,
PRList: this.getStagingDeployCashPRList(issue),
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
+ isTimingDashboardChecked: /-\s\[x]\sI checked the \[App Timing Dashboard]/.test(issue.body),
+ isFirebaseChecked: /-\s\[x]\sI checked \[Firebase Crashlytics]/.test(issue.body),
tag,
};
} catch (exception) {
@@ -178,6 +180,8 @@ class GithubUtils {
* @param {Array} [accessiblePRList] - The list of PR URLs which have passed the accessability check.
* @param {Array} [deployBlockers] - The list of DeployBlocker URLs.
* @param {Array} [resolvedDeployBlockers] - The list of DeployBlockers URLs which have been resolved.
+ * @param {Boolean} [isTimingDashboardChecked]
+ * @param {Boolean} [isFirebaseChecked]
* @returns {Promise}
*/
static generateStagingDeployCashBody(
@@ -187,6 +191,8 @@ class GithubUtils {
accessiblePRList = [],
deployBlockers = [],
resolvedDeployBlockers = [],
+ isTimingDashboardChecked = false,
+ isFirebaseChecked = false,
) {
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
.then((data) => {
@@ -259,6 +265,12 @@ class GithubUtils {
});
}
+ issueBody += '\r\n\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
+ // eslint-disable-next-line max-len
+ issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`;
+
issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
return issueBody;
})
diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml
index fe0eb8aff88a..8553f9b63fbb 100644
--- a/.github/workflows/finishReleaseCycle.yml
+++ b/.github/workflows/finishReleaseCycle.yml
@@ -47,7 +47,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
COMMENT: |
- This issue either has unchecked QA steps or has not yet been marked with the `:shipit:` emoji of approval.
+ This issue either has unchecked items or has not yet been marked with the `:shipit:` emoji of approval.
Reopening!
# Update the production branch to trigger the production deploy.
diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml
index 85ad459978f6..266f998d4f03 100644
--- a/.github/workflows/preDeploy.yml
+++ b/.github/workflows/preDeploy.yml
@@ -216,12 +216,18 @@ jobs:
IS_EXPENSIFY_EMPLOYEE: ${{ fromJSON(steps.checkActor.outputs.isTeamMember) }}
steps:
+ - name: Get merged pull request
+ id: getMergedPullRequest
+ uses: roryabraham/action-get-merged-pull-request@7a7a194f6ff8f3eef58c822083695a97314ebec1
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+
- name: Check whether the actor is member of Expensify/expensify team
id: checkActor
uses: tspascoal/get-user-teams-membership@baf2e6adf4c3b897bd65a7e3184305c165aec872
with:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}
- username: ${{ github.actor }}
+ username: ${{ steps.getMergedPullRequest.outputs.author }}
team: Expensify/expensify
newContributorWelcomeMessage:
diff --git a/.storybook/main.js b/.storybook/main.js
index b229d828bfe8..ad5effb2dbf6 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -6,6 +6,7 @@ module.exports = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y',
+ '@storybook/addon-react-native-web',
],
staticDirs: [
'./public',
diff --git a/android/app/build.gradle b/android/app/build.gradle
index b3bb898ee2e3..97ba80bd0d8d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -152,8 +152,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001015200
- versionName "1.1.52-0"
+ versionCode 1001015400
+ versionName "1.1.54-0"
}
splits {
abi {
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 92b1d3b79059..d310f3305732 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.1.52
+ 1.1.54
CFBundleSignature
????
CFBundleURLTypes
@@ -30,7 +30,7 @@
CFBundleVersion
- 1.1.52.0
+ 1.1.54.0
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 3fd8e939d051..4e8be96624b7 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.1.52
+ 1.1.54
CFBundleSignature
????
CFBundleVersion
- 1.1.52.0
+ 1.1.54.0
diff --git a/package-lock.json b/package-lock.json
index ca2dc8c48c5d..5c7f3b504abd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.1.52-0",
+ "version": "1.1.54-0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -6710,9 +6710,9 @@
"integrity": "sha512-2Y9OXWHRutYmdyW+bNMaFTW8uTBpaaK20xdIFoVtqahEOO9++B+ut3CAWKPZcdRtb9ikG0LUKChGqMeocg0PGA=="
},
"@react-native-picker/picker": {
- "version": "1.9.11",
- "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-1.9.11.tgz",
- "integrity": "sha512-E7whvvMIl9Ln1sxgug7OAEVWQ69n82iV0d2OWWp5VV52+RW3azKh1IFm4rxdW5/oByMfl7FFL0eHNelGgY4BMQ=="
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.3.1.tgz",
+ "integrity": "sha512-DAw1o3bHNRnQPImsK53xCYkgC8bH+t9uTM+0JjNIlmMwvVLFnmDxi9v5iBTIWFMWG/pUHQaw66E29wW6oJG9Tw=="
},
"@react-native/assets": {
"version": "1.0.0",
@@ -8454,6 +8454,12 @@
}
}
},
+ "@storybook/addon-react-native-web": {
+ "version": "0.0.18",
+ "resolved": "https://registry.npmjs.org/@storybook/addon-react-native-web/-/addon-react-native-web-0.0.18.tgz",
+ "integrity": "sha512-tMZumiF+Dgk7sngMWFrbDdBN3h5C001traYM46CqNQvtJoZvwYe2MPGEeWy3wk+5D8vOv8TFw2i96f3wwfralg==",
+ "dev": true
+ },
"@storybook/addon-toolbars": {
"version": "6.4.12",
"resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-6.4.12.tgz",
diff --git a/package.json b/package.json
index b6ed2db2746d..468a96150c38 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.1.52-0",
+ "version": "1.1.54-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -50,7 +50,7 @@
"@react-native-firebase/crashlytics": "^12.3.0",
"@react-native-firebase/perf": "^12.3.0",
"@react-native-masked-view/masked-view": "^0.2.4",
- "@react-native-picker/picker": "^1.9.11",
+ "@react-native-picker/picker": "^2.3.1",
"@react-navigation/compat": "^5.3.20",
"@react-navigation/drawer": "6.1.8",
"@react-navigation/native": "6.0.8",
@@ -126,6 +126,7 @@
"@react-native-community/eslint-config": "^2.0.0",
"@storybook/addon-a11y": "^6.4.12",
"@storybook/addon-essentials": "^6.4.12",
+ "@storybook/addon-react-native-web": "0.0.18",
"@storybook/addons": "^6.4.12",
"@storybook/react": "^6.4.12",
"@storybook/theming": "^6.4.12",
diff --git a/src/CONST.js b/src/CONST.js
index 4ffeaba8f616..de79fdcb71cc 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -380,6 +380,8 @@ const CONST = {
DECIMAL_PAD: 'decimal-pad',
},
+ ATTACHMENT_SOURCE_ATTRIBUTE: 'data-expensify-source',
+
ATTACHMENT_PICKER_TYPE: {
FILE: 'file',
IMAGE: 'image',
diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index f3837f8ad61d..39d1a8f9dd09 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -243,7 +243,7 @@ class AddPlaidBankAccount extends React.Component {
{
+ onInputChange={(index) => {
this.setState({selectedIndex: Number(index)});
this.clearError('selectedBank');
}}
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js
index 4fe540bcc813..21ec2d724f15 100755
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.js
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js
@@ -256,6 +256,9 @@ class EmojiPickerMenu extends Component {
const isHeader = e => e.header || e.spacer;
do {
newIndex += steps;
+ if (newIndex < 0) {
+ break;
+ }
} while (isHeader(this.state.filteredEmojis[newIndex]));
};
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js
index 6f53780a4eb4..e768e59bbf98 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js
@@ -16,7 +16,7 @@ const AnchorRenderer = (props) => {
const htmlAttribs = props.tnode.attributes;
// An auth token is needed to download Expensify chat attachments
- const isAttachment = Boolean(htmlAttribs['data-expensify-source']);
+ const isAttachment = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]);
const displayName = lodashGet(props.tnode, 'domNode.children[0].data', '');
const parentStyle = lodashGet(props.tnode, 'parent.styles.nativeTextRet', {});
const attrHref = htmlAttribs.href || '';
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
index e1f9b356ab97..152e2f03749c 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
@@ -5,6 +5,7 @@ import AttachmentModal from '../../AttachmentModal';
import styles from '../../../styles/styles';
import ThumbnailImage from '../../ThumbnailImage';
import TouchableWithoutFocus from '../../TouchableWithoutFocus';
+import CONST from '../../../CONST';
const ImageRenderer = (props) => {
const htmlAttribs = props.tnode.attributes;
@@ -14,7 +15,7 @@ const ImageRenderer = (props) => {
// - Chat Attachment images
//
// Images uploaded by the user via the app or email.
- // These have a full-sized image `htmlAttribs['data-expensify-source']`
+ // These have a full-sized image `htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]`
// and a thumbnail `htmlAttribs.src`. Both of these URLs need to have
// an authToken added to them in order to control who
// can see the images.
@@ -26,11 +27,11 @@ const ImageRenderer = (props) => {
// Concierge responder attachments are uploaded to S3 without any access
// control and thus require no authToken to verify access.
//
- const isAttachment = Boolean(htmlAttribs['data-expensify-source']);
+ const isAttachment = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]);
const originalFileName = htmlAttribs['data-name'];
let previewSource = htmlAttribs.src;
let source = isAttachment
- ? htmlAttribs['data-expensify-source']
+ ? htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]
: htmlAttribs.src;
// Update the image URL so the images can be accessed depending on the config environment
diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js
index 30eba46aba9e..aafb06ea6fd2 100644
--- a/src/components/LocalePicker.js
+++ b/src/components/LocalePicker.js
@@ -49,7 +49,7 @@ const LocalePicker = (props) => {
return (
{
+ onInputChange={(locale) => {
if (locale === props.preferredLocale) {
return;
}
diff --git a/src/components/Picker/BasePicker/basePickerPropTypes.js b/src/components/Picker/BasePicker/basePickerPropTypes.js
index 02f5ff2d2b6d..7af66b59bdf6 100644
--- a/src/components/Picker/BasePicker/basePickerPropTypes.js
+++ b/src/components/Picker/BasePicker/basePickerPropTypes.js
@@ -6,7 +6,7 @@ import * as Expensicons from '../../Icon/Expensicons';
const propTypes = {
/** A callback method that is called when the value changes and it received the selected value as an argument */
- onChange: PropTypes.func.isRequired,
+ onInputChange: PropTypes.func.isRequired,
/** Whether or not to show the disabled styles */
disabled: PropTypes.bool,
diff --git a/src/components/Picker/BasePicker/index.js b/src/components/Picker/BasePicker/index.js
index 75d06cfbc84c..a56c7d9f0d29 100644
--- a/src/components/Picker/BasePicker/index.js
+++ b/src/components/Picker/BasePicker/index.js
@@ -1,32 +1,53 @@
import React from 'react';
import RNPickerSelect from 'react-native-picker-select';
+import _ from 'underscore';
import styles from '../../../styles/styles';
import * as basePickerPropTypes from './basePickerPropTypes';
import basePickerStyles from './basePickerStyles';
-const BasePicker = props => (
- props.icon(props.size)}
- disabled={props.disabled}
- fixAndroidTouchableBug
- onOpen={props.onOpen}
- onClose={props.onClose}
- pickerProps={{
- onFocus: props.onOpen,
- onBlur: props.onClose,
- }}
- />
-);
+class BasePicker extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ selectedValue: this.props.value || this.props.defaultValue,
+ };
+
+ this.updateSelectedValueAndExecuteOnChange = this.updateSelectedValueAndExecuteOnChange.bind(this);
+ }
+
+ updateSelectedValueAndExecuteOnChange(value) {
+ this.props.onInputChange(value);
+ this.setState({selectedValue: value});
+ }
+
+ render() {
+ const hasError = !_.isEmpty(this.props.errorText);
+ return (
+ this.props.icon(this.props.size)}
+ disabled={this.props.disabled}
+ fixAndroidTouchableBug
+ onOpen={this.props.onOpen}
+ onClose={this.props.onClose}
+ pickerProps={{
+ onFocus: this.props.onOpen,
+ onBlur: this.props.onBlur,
+ ref: this.props.innerRef,
+ }}
+ />
+ );
+ }
+}
BasePicker.propTypes = basePickerPropTypes.propTypes;
BasePicker.defaultProps = basePickerPropTypes.defaultProps;
-BasePicker.displayName = 'BasePicker';
export default BasePicker;
diff --git a/src/components/Picker/index.js b/src/components/Picker/index.js
index b7277fcda73e..5f2f86374854 100644
--- a/src/components/Picker/index.js
+++ b/src/components/Picker/index.js
@@ -6,6 +6,7 @@ import BasePicker from './BasePicker';
import Text from '../Text';
import styles from '../../styles/styles';
import InlineErrorText from '../InlineErrorText';
+import * as FormUtils from '../../libs/FormUtils';
const propTypes = {
/** Picker label */
@@ -14,22 +15,39 @@ const propTypes = {
/** Should the picker appear disabled? */
isDisabled: PropTypes.bool,
- /** Should the input be styled for errors */
- hasError: PropTypes.bool,
+ /** Input value */
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** Error text to display */
errorText: PropTypes.string,
/** Customize the Picker container */
containerStyles: PropTypes.arrayOf(PropTypes.object),
+
+ /** Indicates that the input is being used with the Form component */
+ isFormInput: PropTypes.bool,
+
+ /**
+ * The ID used to uniquely identify the input
+ *
+ * @param {Object} props - props passed to the input
+ * @returns {Object} - returns an Error object if isFormInput is supplied but inputID is falsey or not a string
+ */
+ inputID: props => FormUtils.validateInputIDProps(props),
+
+ /** Saves a draft of the input value when used in a form */
+ shouldSaveDraft: PropTypes.bool,
};
const defaultProps = {
label: '',
isDisabled: false,
- hasError: false,
errorText: '',
containerStyles: [],
+ isFormInput: false,
+ inputID: undefined,
+ shouldSaveDraft: false,
+ value: undefined,
};
class Picker extends PureComponent {
@@ -48,6 +66,7 @@ class Picker extends PureComponent {
style={[
styles.pickerContainer,
this.props.isDisabled && styles.inputDisabled,
+ ...this.props.containerStyles,
]}
>
{this.props.label && (
@@ -58,12 +77,13 @@ class Picker extends PureComponent {
onClose={() => this.setState({isOpen: false})}
disabled={this.props.isDisabled}
focused={this.state.isOpen}
- hasError={this.props.hasError}
+ errorText={this.props.errorText}
+ value={this.props.value}
// eslint-disable-next-line react/jsx-props-no-spreading
{...pickerProps}
/>
-
+
{this.props.errorText}
>
@@ -74,4 +94,5 @@ class Picker extends PureComponent {
Picker.propTypes = propTypes;
Picker.defaultProps = defaultProps;
-export default Picker;
+// eslint-disable-next-line react/jsx-props-no-spreading
+export default React.forwardRef((props, ref) => );
diff --git a/src/components/StatePicker.js b/src/components/StatePicker.js
index d9fbe810eeb4..11d4cad09f55 100644
--- a/src/components/StatePicker.js
+++ b/src/components/StatePicker.js
@@ -32,7 +32,7 @@ const StatePicker = props => (
option.login !== searchValue)
&& ((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue)) || Str.isValidPhone(searchValue))
- && (!_.find(loginOptionsToExclude, loginOptionToExclude => loginOptionToExclude.login === searchValue.toLowerCase()))
+ && (!_.find(loginOptionsToExclude, loginOptionToExclude => loginOptionToExclude.login === addSMSDomainIfPhoneNumber(searchValue).toLowerCase()))
&& (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas))
) {
// If the phone number doesn't have an international code then let's prefix it with the
@@ -788,7 +789,7 @@ function getHeaderMessage(hasSelectableOptions, hasUserToInvite, searchValue, ma
// Without a search value, it would be very confusing to see a search validation message.
// Therefore, this skips the validation when there is no search value.
if (searchValue && !hasSelectableOptions && !hasUserToInvite) {
- if (/^\d+$/.test(searchValue)) {
+ if (/^\d+$/.test(searchValue) && !Str.isValidPhone(searchValue)) {
return Localize.translate(preferredLocale, 'messages.errorMessageInvalidPhone');
}
diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js
index ba410c460829..d98f62c4aa21 100644
--- a/src/libs/actions/Report.js
+++ b/src/libs/actions/Report.js
@@ -193,7 +193,7 @@ function getSimplifiedReportObject(report) {
const createTimestamp = lodashGet(report, 'lastActionCreated', 0);
const lastMessageTimestamp = moment.utc(createTimestamp).unix();
const lastActionMessage = lodashGet(report, ['lastActionMessage', 'html'], '');
- const isLastMessageAttachment = /
]+)\/>/gi.test(lastActionMessage);
+ const isLastMessageAttachment = new RegExp(`
]*${CONST.ATTACHMENT_SOURCE_ATTRIBUTE}\\s*=\\s*"[^"]*"[^>]*>`, 'gi').test(lastActionMessage);
const chatType = lodashGet(report, ['reportNameValuePairs', 'chatType'], '');
let lastMessageText = null;
diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js
index 31847224d9dc..6a95f2e56b44 100644
--- a/src/pages/ReimbursementAccount/CompanyStep.js
+++ b/src/pages/ReimbursementAccount/CompanyStep.js
@@ -268,7 +268,7 @@ class CompanyStep extends React.Component {
({value, label}))}
- onChange={value => this.clearErrorAndSetValue('incorporationType', value)}
+ onInputChange={value => this.clearErrorAndSetValue('incorporationType', value)}
value={this.state.incorporationType}
placeholder={{value: '', label: '-'}}
hasError={this.getErrors().incorporationType}
diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js
index 6ee169d0256d..a8a213d290cf 100644
--- a/src/pages/ReportSettingsPage.js
+++ b/src/pages/ReportSettingsPage.js
@@ -168,7 +168,7 @@ class ReportSettingsPage extends Component {
{
+ onInputChange={(notificationPreference) => {
Report.updateNotificationPreference(
this.props.report.reportID,
notificationPreference,
diff --git a/src/pages/settings/PreferencesPage.js b/src/pages/settings/PreferencesPage.js
index 4752dfc291d8..ae7de7e4f51f 100755
--- a/src/pages/settings/PreferencesPage.js
+++ b/src/pages/settings/PreferencesPage.js
@@ -85,7 +85,7 @@ const PreferencesPage = (props) => {
NameValuePair.set(CONST.NVP.PRIORITY_MODE, mode, ONYXKEYS.NVP_PRIORITY_MODE)
}
items={_.values(priorityModes)}
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index cf4ecd2e5ce2..1dc7a502e788 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -250,7 +250,7 @@ class ProfilePage extends Component {
{
+ onInputChange={(pronouns) => {
const hasSelfSelectedPronouns = pronouns === CONST.PRONOUNS.SELF_SELECT;
this.setState({
pronouns: hasSelfSelectedPronouns ? '' : pronouns,
@@ -288,7 +288,7 @@ class ProfilePage extends Component {
this.setState({selectedTimezone})}
+ onInputChange={selectedTimezone => this.setState({selectedTimezone})}
items={timezones}
isDisabled={this.state.isAutomaticTimezone}
value={this.state.selectedTimezone}
diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js
index 780bf75f487e..a31dad1708c5 100644
--- a/src/pages/workspace/WorkspaceNewRoomPage.js
+++ b/src/pages/workspace/WorkspaceNewRoomPage.js
@@ -173,7 +173,7 @@ class WorkspaceNewRoomPage extends React.Component {
items={this.state.workspaceOptions}
errorText={this.state.errors.policyID}
hasError={Boolean(this.state.errors.policyID)}
- onChange={policyID => this.clearErrorAndSetValue('policyID', policyID)}
+ onInputChange={policyID => this.clearErrorAndSetValue('policyID', policyID)}
/>
@@ -181,7 +181,7 @@ class WorkspaceNewRoomPage extends React.Component {
value={this.state.visibility}
label={this.props.translate('newRoomPage.visibility')}
items={visibilityOptions}
- onChange={visibility => this.setState({visibility})}
+ onInputChange={visibility => this.setState({visibility})}
/>
diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js
index ee9989d86ff3..a0bb45fcbe6b 100644
--- a/src/pages/workspace/WorkspaceSettingsPage.js
+++ b/src/pages/workspace/WorkspaceSettingsPage.js
@@ -168,7 +168,7 @@ class WorkspaceSettingsPage extends React.Component {
this.setState({currency})}
+ onInputChange={currency => this.setState({currency})}
items={this.getCurrencyItems()}
value={this.state.currency}
isDisabled={hasVBA}
diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseNoVBAView.js b/src/pages/workspace/reimburse/WorkspaceReimburseNoVBAView.js
index 6e68c98488cd..e068a8666eeb 100644
--- a/src/pages/workspace/reimburse/WorkspaceReimburseNoVBAView.js
+++ b/src/pages/workspace/reimburse/WorkspaceReimburseNoVBAView.js
@@ -181,7 +181,7 @@ class WorkspaceReimburseNoVBAView extends React.Component {
label={this.props.translate('workspace.reimburse.trackDistanceUnit')}
items={this.unitItems}
value={this.state.unitValue}
- onChange={value => this.setUnit(value)}
+ onInputChange={value => this.setUnit(value)}
/>
diff --git a/src/stories/Form.stories.js b/src/stories/Form.stories.js
index 7de80c6192af..e0e551bb17c9 100644
--- a/src/stories/Form.stories.js
+++ b/src/stories/Form.stories.js
@@ -1,6 +1,7 @@
import React, {useState} from 'react';
import {View} from 'react-native';
import TextInput from '../components/TextInput';
+import Picker from '../components/Picker';
import AddressSearch from '../components/AddressSearch';
import Form from '../components/Form';
import * as FormActions from '../libs/actions/FormActions';
@@ -16,7 +17,12 @@ import Text from '../components/Text';
const story = {
title: 'Components/Form',
component: Form,
- subcomponents: {TextInput, AddressSearch, CheckboxWithLabel},
+ subcomponents: {
+ TextInput,
+ AddressSearch,
+ CheckboxWithLabel,
+ Picker,
+ },
};
const Template = (args) => {
@@ -50,6 +56,49 @@ const Template = (args) => {
containerStyles={[styles.mt4]}
isFormInput
/>
+
+
+
+
;
+
+// Arguments can be passed to the component by binding
+// See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
+
+const Default = Template.bind({});
+Default.args = {
+ label: 'Default picker',
+ name: 'Default',
+ onInputChange: () => {},
+ items: [
+ {
+ label: 'Orange',
+ value: 'orange',
+ },
+ {
+ label: 'Apple',
+ value: 'apple',
+ },
+ ],
+};
+
+const PickerWithValue = Template.bind({});
+PickerWithValue.args = {
+ label: 'Picker with defined value',
+ name: 'Picker with defined value',
+ onInputChange: () => {},
+ value: 'apple',
+ items: [
+ {
+ label: 'Orange',
+ value: 'orange',
+ },
+ {
+ label: 'Apple',
+ value: 'apple',
+ },
+ ],
+};
+
+const ErrorStory = Template.bind({});
+ErrorStory.args = {
+ label: 'Picker with error',
+ name: 'PickerWithError',
+ errorText: 'This field has an error.',
+ onInputChange: () => {},
+ items: [
+ {
+ label: 'Orange',
+ value: 'orange',
+ },
+ {
+ label: 'Apple',
+ value: 'apple',
+ },
+ ],
+};
+
+const Disabled = Template.bind({});
+Disabled.args = {
+ label: 'Picker disabled',
+ name: 'Disabled',
+ isDisabled: true,
+ onInputChange: () => {},
+ items: [
+ {
+ label: 'Orange',
+ value: 'orange',
+ },
+ {
+ label: 'Apple',
+ value: 'apple',
+ },
+ ],
+};
+
+export default story;
+export {
+ Default,
+ PickerWithValue,
+ ErrorStory,
+ Disabled,
+};
diff --git a/tests/unit/GithubUtilsTest.js b/tests/unit/GithubUtilsTest.js
index ef8818a293ac..a5c8e8a2687f 100644
--- a/tests/unit/GithubUtilsTest.js
+++ b/tests/unit/GithubUtilsTest.js
@@ -68,6 +68,8 @@ describe('GithubUtils', () => {
url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29',
number: 29,
deployBlockers: [],
+ isTimingDashboardChecked: false,
+ isFirebaseChecked: false,
};
const expectedResponseWithDeployBlockers = {...baseExpectedResponse};
expectedResponseWithDeployBlockers.deployBlockers = [
@@ -375,6 +377,11 @@ describe('GithubUtils', () => {
const lineBreakDouble = '\r\n\r\n';
const indent = ' ';
const assignOctocatHubot = ' - @octocat @hubot';
+ const deployerVerificationsHeader = '\r\n**Deployer verifications:**';
+ // eslint-disable-next-line max-len
+ const timingDashboardVerification = 'I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.';
+ // eslint-disable-next-line max-len
+ const firebaseVerification = 'I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.';
// Valid output which will be reused in the deploy blocker tests
const allVerifiedExpectedOutput = `${baseExpectedOutput}`
@@ -394,6 +401,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${listStart}${basePRList[1]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${openCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[5]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[6]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -409,6 +419,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${listStart}${basePRList[1]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${openCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[5]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[6]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -424,6 +437,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${listStart}${basePRList[1]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[5]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[6]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -433,7 +449,11 @@ describe('GithubUtils', () => {
githubUtils.generateStagingDeployCashBody(tag, basePRList, basePRList)
.then((issueBody) => {
expect(issueBody).toBe(
- `${allVerifiedExpectedOutput}${lineBreakDouble}${ccApplauseLeads}`,
+ `${allVerifiedExpectedOutput}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ + `${lineBreakDouble}${ccApplauseLeads}`,
);
})
));
@@ -446,6 +466,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${deployBlockerHeader}`
+ `${lineBreak}${openCheckbox}${baseDeployBlockerList[0]}`
+ `${lineBreak}${openCheckbox}${baseDeployBlockerList[1]}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -459,6 +482,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${deployBlockerHeader}`
+ `${lineBreak}${closedCheckbox}${baseDeployBlockerList[0]}`
+ `${lineBreak}${openCheckbox}${baseDeployBlockerList[1]}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -477,6 +503,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${deployBlockerHeader}`
+ `${lineBreak}${closedCheckbox}${baseDeployBlockerList[0]}`
+ `${lineBreak}${closedCheckbox}${baseDeployBlockerList[1]}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -495,6 +524,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${internalQAHeader}`
+ `${lineBreak}${openCheckbox}${internalQAPRList[0]}${assignOctocatHubot}`
+ `${lineBreak}${openCheckbox}${internalQAPRList[1]}${assignOctocatHubot}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
@@ -513,6 +545,9 @@ describe('GithubUtils', () => {
+ `${lineBreakDouble}${internalQAHeader}`
+ `${lineBreak}${closedCheckbox}${internalQAPRList[0]}${assignOctocatHubot}`
+ `${lineBreak}${openCheckbox}${internalQAPRList[1]}${assignOctocatHubot}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
);
})
diff --git a/tests/unit/createOrUpdateStagingDeployTest.js b/tests/unit/createOrUpdateStagingDeployTest.js
index 90307c3f469c..3d9d5400e525 100644
--- a/tests/unit/createOrUpdateStagingDeployTest.js
+++ b/tests/unit/createOrUpdateStagingDeployTest.js
@@ -102,6 +102,11 @@ const closedCheckbox = '- [x] ';
const listStart = '- ';
const QA = 'QA';
const accessibility = 'Accessibility';
+const deployerVerificationsHeader = '\r\n**Deployer verifications:**';
+// eslint-disable-next-line max-len
+const timingDashboardVerification = 'I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.';
+// eslint-disable-next-line max-len
+const firebaseVerification = 'I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.';
const ccApplauseLeads = 'cc @Expensify/applauseleads\r\n';
const deployBlockerHeader = '\r\n**Deploy Blockers:**';
const lineBreak = '\r\n';
@@ -169,6 +174,9 @@ describe('createOrUpdateStagingDeployCash', () => {
+ `${lineBreakDouble}${listStart}${basePRList[5]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${openCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[6]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${openCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[7]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${openCheckbox}${accessibility}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
});
});
@@ -189,6 +197,9 @@ describe('createOrUpdateStagingDeployCash', () => {
+ `${lineBreak}${openCheckbox}${basePRList[5]}`
+ `${lineBreak}${openCheckbox}${basePRList[8]}`
+ `${lineBreak}${closedCheckbox}${basePRList[9]}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${closedCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${closedCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
state: 'open',
};
@@ -285,12 +296,23 @@ describe('createOrUpdateStagingDeployCash', () => {
+ `${lineBreak}${closedCheckbox}${basePRList[9]}`
+ `${lineBreak}${openCheckbox}${basePRList[10]}`
+ `${lineBreak}${openCheckbox}${basePRList[11]}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+
+ // Note: these will be unchecked with a new app version, and that's intentional
+ + `${lineBreak}${openCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${openCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
});
});
});
test('without NPM_VERSION input, just a new deploy blocker', () => {
+ mockGetInput.mockImplementation((arg) => {
+ if (arg !== 'GITHUB_TOKEN') {
+ return;
+ }
+ return 'fake_token';
+ });
mockGetPullRequestsMergedBetween.mockImplementation((fromRef, toRef) => {
if (fromRef === '1.0.1-0' && toRef === '1.0.2-2') {
return [
@@ -335,7 +357,7 @@ describe('createOrUpdateStagingDeployCash', () => {
// eslint-disable-next-line max-len
html_url: `https://github.com/Expensify/App/issues/${openStagingDeployCashBefore.number}`,
// eslint-disable-next-line max-len
- body: `${baseExpectedOutput('1.0.2-2')}`
+ body: `${baseExpectedOutput('1.0.2-1')}`
+ `${lineBreakDouble}${listStart}${basePRList[5]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[6]}${lineBreak}${indent}${closedCheckbox}${QA}${lineBreak}${indent}${closedCheckbox}${accessibility}`
+ `${lineBreakDouble}${listStart}${basePRList[7]}${lineBreak}${indent}${openCheckbox}${QA}${lineBreak}${indent}${openCheckbox}${accessibility}`
@@ -345,6 +367,9 @@ describe('createOrUpdateStagingDeployCash', () => {
+ `${lineBreak}${closedCheckbox}${basePRList[9]}`
+ `${lineBreak}${openCheckbox}${baseIssueList[0]}`
+ `${lineBreak}${openCheckbox}${baseIssueList[1]}`
+ + `${lineBreak}${deployerVerificationsHeader}`
+ + `${lineBreak}${closedCheckbox}${timingDashboardVerification}`
+ + `${lineBreak}${closedCheckbox}${firebaseVerification}`
+ `${lineBreakDouble}${ccApplauseLeads}`,
});
});