@qualtrics/plugin-ui-react 1.3.3 → 1.4.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +10 -0
- package/.eslintrc.js +1 -1
- package/Dockerfile +18 -0
- package/Makefile +33 -4
- package/README.md +28 -0
- package/dist/common/capture-event/capture-event.d.ts +13 -0
- package/dist/common/capture-event/capture-event.test.d.ts +1 -0
- package/dist/common/copy-text/copy-text.d.ts +8 -0
- package/dist/common/copy-text/copy-text.test.d.ts +1 -0
- package/dist/common/field-mapper/field-mapper-key/field-mapper-key.d.ts +12 -0
- package/dist/common/field-mapper/field-mapper-row/field-mapper-row.d.ts +17 -0
- package/dist/common/field-mapper/field-mapper-value/field-mapper-value.d.ts +11 -0
- package/dist/common/field-mapper/field-mapper.d.ts +20 -0
- package/dist/common/field-mapper/index.d.ts +4 -0
- package/dist/common/piped-text-input/piped-text-input.d.ts +2 -2
- package/dist/common/piped-text-select/piped-text-menu/piped-text-menu-item/piped-text-menu-item.d.ts +16 -0
- package/dist/common/piped-text-select/piped-text-menu/piped-text-submenu-item/piped-text-submenu-item.d.ts +13 -0
- package/dist/common/portal.d.ts +3 -0
- package/dist/hooks/use-id.d.ts +7 -0
- package/dist/index.d.ts +4 -1
- package/dist/lib/models/field-mapper-item.d.ts +7 -0
- package/dist/lib/models/piped-text-menu-item-data.d.ts +21 -0
- package/dist/lib/models/piped-text.d.ts +16 -0
- package/dist/lib/type-validation/validate-primitives.d.ts +1 -0
- package/dist/main.es.js +959 -172
- package/dist/main.js +963 -170
- package/jenkins.groovy +284 -0
- package/jenkinsPipelineUtils/jira.groovy +84 -0
- package/jenkinsPipelineUtils/notifications.groovy +34 -0
- package/jenkinsPipelineUtils/pipelineControls.groovy +56 -0
- package/jenkinsPipelineUtils/trs.groovy +30 -0
- package/package.json +7 -6
- package/test_results/junit.xml +101 -0
- package/.bee +0 -2
- package/Jenkinsfile +0 -109
- package/bee +0 -932
package/jenkins.groovy
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Job Parameters
|
|
2
|
+
JOB_PARAMETER_RELEASE_STEP_PUBLISH = 'publish'
|
|
3
|
+
JOB_PARAMETER_RELEASE_STEP_PC_TICKETS = 'pc tickets'
|
|
4
|
+
|
|
5
|
+
JOB_PARAMETER_PROJECT_PLUGIN_UI_REACT = 'plugin-ui-react'
|
|
6
|
+
|
|
7
|
+
PUBLIC_REGISTRY = 'https://registry.npmjs.org'
|
|
8
|
+
|
|
9
|
+
// Project Contstants
|
|
10
|
+
SLACK_DEPLOY_CHANNEL = '#app-sdk-deploys'
|
|
11
|
+
SLACK_MR_VALIDATION_CHANNEL = '#app-sdk-mr-validation'
|
|
12
|
+
|
|
13
|
+
PROJECTS = [
|
|
14
|
+
[
|
|
15
|
+
name: '@qualtrics/plugin-ui-react',
|
|
16
|
+
directory: '.',
|
|
17
|
+
jobParameterProject: JOB_PARAMETER_PROJECT_PLUGIN_UI_REACT,
|
|
18
|
+
trsId: '1d15e1dc-f156-45a2-966e-89dba1797e68',
|
|
19
|
+
trsServiceTag: 'qualtrics-plugin-ui-react'
|
|
20
|
+
]
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
TRS_TEAM = 'appsdk'
|
|
24
|
+
TRS_GRAFANA_PROJECT_URL = "https://grafana.eng.qops.net/d/2qriuzfMk/trs-unit-tests-by-service?orgId=1&refresh=1h&var-unit_team=${TRS_TEAM}"
|
|
25
|
+
TEST_RESULTS_FILE = 'junit.xml'
|
|
26
|
+
CODE_COVERAGE_FILE = 'cobertura-coverage.xml'
|
|
27
|
+
|
|
28
|
+
JIRA_SERVICE_TEAM_ID = '18254'
|
|
29
|
+
JIRA_LABELS = ['DPEE']
|
|
30
|
+
TEAM_RUNBOOK = 'https://odo.corp.qualtrics.com/wiki/index.php/AppSDK_Team_Runbook'
|
|
31
|
+
TEAM_ROLLBACK = 'https://odo.corp.qualtrics.com/wiki/index.php/AppSDK_Team_Runbook#Task_Rollback'
|
|
32
|
+
|
|
33
|
+
MAKE_PUBLISH_EXTERNAL = 'make publish-external'
|
|
34
|
+
MAKE_BUILD = 'make build'
|
|
35
|
+
MAKE_TEST = 'make test'
|
|
36
|
+
MAKE_CLEAN = 'make clean'
|
|
37
|
+
|
|
38
|
+
MAIN_BRANCH = 'master'
|
|
39
|
+
|
|
40
|
+
pipeline {
|
|
41
|
+
agent {
|
|
42
|
+
label 'nomad-builder'
|
|
43
|
+
}
|
|
44
|
+
environment {
|
|
45
|
+
DOCKER_HOST = '127.0.0.1:2375'
|
|
46
|
+
WORKSPACE_PATH = "/var/lib/nomad/alloc/${NOMAD_ALLOC_ID}/${NOMAD_TASK_NAME}${WORKSPACE}"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
options {
|
|
50
|
+
timestamps()
|
|
51
|
+
buildDiscarder(logRotator(numToKeepStr: '5'))
|
|
52
|
+
timeout(time: 60, unit: 'MINUTES')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stages {
|
|
56
|
+
stage('Setup') {
|
|
57
|
+
steps {
|
|
58
|
+
script {
|
|
59
|
+
def rootDir = pwd()
|
|
60
|
+
def jenkinsUtilsFolder = "${rootDir}/jenkinsPipelineUtils"
|
|
61
|
+
trs = load("${jenkinsUtilsFolder}/trs.groovy")
|
|
62
|
+
jira = load("${jenkinsUtilsFolder}/jira.groovy")
|
|
63
|
+
notifications = load("${jenkinsUtilsFolder}/notifications.groovy")
|
|
64
|
+
pipelineControls = load("${jenkinsUtilsFolder}/pipelineControls.groovy")
|
|
65
|
+
|
|
66
|
+
notifications.setCommitStatus('running')
|
|
67
|
+
|
|
68
|
+
PROJECT_NAME_VERSION = ''
|
|
69
|
+
|
|
70
|
+
for (project in PROJECTS) {
|
|
71
|
+
def nameVersion = getProjectNameAndVersion(project.name, project.directory)
|
|
72
|
+
PROJECT_NAME_VERSION += "${nameVersion} "
|
|
73
|
+
TRS_GRAFANA_PROJECT_URL += "&var-unit_service=${project.trsServiceTag}"
|
|
74
|
+
echo "Project: ${nameVersion}"
|
|
75
|
+
}
|
|
76
|
+
echo "Workspace: ${WORKSPACE_PATH}"
|
|
77
|
+
echo "Git action: ${env.gitlabActionType}"
|
|
78
|
+
echo "Git source branch: ${env.gitlabSourceBranch}"
|
|
79
|
+
|
|
80
|
+
currentBuild.displayName = pipelineControls.getJobDisplayName(currentBuild.number, PROJECT_NAME_VERSION, MAIN_BRANCH)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
stage('Build') {
|
|
85
|
+
when {
|
|
86
|
+
expression {
|
|
87
|
+
!isManualPCTicketJob()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
steps {
|
|
91
|
+
sh script: MAKE_BUILD
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
stage('Unit Test') {
|
|
95
|
+
when {
|
|
96
|
+
expression {
|
|
97
|
+
!isManualPCTicketJob()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
steps {
|
|
101
|
+
script {
|
|
102
|
+
for (project in PROJECTS) {
|
|
103
|
+
echo "testing ${project.name}"
|
|
104
|
+
sh script: "${MAKE_TEST} DIRECTORY=${project.directory}"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
stage('TRS') {
|
|
110
|
+
when {
|
|
111
|
+
expression {
|
|
112
|
+
pipelineControls.isPushToMain(MAIN_BRANCH)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
steps {
|
|
116
|
+
script {
|
|
117
|
+
for (project in PROJECTS) {
|
|
118
|
+
publishToTRS(project)
|
|
119
|
+
}
|
|
120
|
+
echo "To see the TRS dashboard vist ${TRS_GRAFANA_PROJECT_URL}"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
stage('PC Tickets') {
|
|
125
|
+
when {
|
|
126
|
+
expression {
|
|
127
|
+
isManualPCTicketJob()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
steps {
|
|
131
|
+
script {
|
|
132
|
+
for (project in PROJECTS) {
|
|
133
|
+
if (project.jobParameterProject == JOB_PARAMETER_PROJECT) {
|
|
134
|
+
createPCReleaseTicket(project)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
stage('Publish') {
|
|
141
|
+
when {
|
|
142
|
+
expression {
|
|
143
|
+
pipelineControls.isPushToMain(MAIN_BRANCH) || JOB_PARAMETER_RELEASE_STEP == JOB_PARAMETER_RELEASE_STEP_PUBLISH
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
steps {
|
|
147
|
+
script {
|
|
148
|
+
for (project in PROJECTS) {
|
|
149
|
+
if (isDeploymentJob(project.jobParameterProject, project.directory)) {
|
|
150
|
+
publishToNpm(project)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
post {
|
|
159
|
+
success {
|
|
160
|
+
script {
|
|
161
|
+
if (!isManualPCTicketJob()) {
|
|
162
|
+
def validationChannel = pipelineControls.isMergeRequest(MAIN_BRANCH) ? SLACK_MR_VALIDATION_CHANNEL : SLACK_DEPLOY_CHANNEL
|
|
163
|
+
notifications.sendSlackVerifiedProjectMessage(PROJECT_NAME_VERSION, validationChannel)
|
|
164
|
+
}
|
|
165
|
+
notifications.setCommitStatus('success')
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
failure {
|
|
169
|
+
script {
|
|
170
|
+
def validationChannel = pipelineControls.isMergeRequest(MAIN_BRANCH) ? SLACK_MR_VALIDATION_CHANNEL : SLACK_DEPLOY_CHANNEL
|
|
171
|
+
notifications.sendSlackFailedToVerifyProjectMessage(PROJECT_NAME_VERSION, validationChannel)
|
|
172
|
+
notifications.setCommitStatus('failed')
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
aborted {
|
|
176
|
+
script {
|
|
177
|
+
notifications.setCommitStatus('canceled')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
always {
|
|
181
|
+
sh script: MAKE_CLEAN
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
def publishToTRS(project) {
|
|
187
|
+
echo "publishing ${project.name} to TRS"
|
|
188
|
+
def resultsDir = "./coverage/${project.directory}/${TEST_RESULTS_FILE}"
|
|
189
|
+
def coverageDir = "./coverage/${project.directory}/${CODE_COVERAGE_FILE}"
|
|
190
|
+
trs.publishResultsToTRS(project.trsId, project.trsServiceTag, resultsDir, coverageDir, WORKSPACE_PATH, TRS_TEAM)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def createPCReleaseTicket(project) {
|
|
194
|
+
def nameVersion = getProjectNameAndVersion(project.name, project.directory)
|
|
195
|
+
echo "creating PC ticket for ${nameVersion}"
|
|
196
|
+
|
|
197
|
+
def pcTicketUrl = jira.createPCTicket(nameVersion, JIRA_SERVICE_TEAM_ID, TEAM_RUNBOOK, TEAM_ROLLBACK, JIRA_LABELS)
|
|
198
|
+
notifications.sendPCTicketCreatedMessage(pcTicketUrl, nameVersion, SLACK_DEPLOY_CHANNEL)
|
|
199
|
+
|
|
200
|
+
echo "PC ticket for ${nameVersion} was created"
|
|
201
|
+
echo "PC ticket URL: ${pcTicketUrl}"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
def publishToNpm(project) {
|
|
205
|
+
def nameVersion = getProjectNameAndVersion(project.name, project.directory)
|
|
206
|
+
echo "publishing ${nameVersion}"
|
|
207
|
+
|
|
208
|
+
if (hasVersionBeenPublished(project)) {
|
|
209
|
+
def alreadyPublishedMessage = "${nameVersion} has already been published."
|
|
210
|
+
if (isManualDeployJob()) {
|
|
211
|
+
notifications.sendSlackFailedToPublishMessage(nameVersion, SLACK_DEPLOY_CHANNEL)
|
|
212
|
+
erorr alreadyPublishedMessage
|
|
213
|
+
}
|
|
214
|
+
echo "${alreadyPublishedMessage} Skipping..."
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
def packageJsonPath = getPackageJsonPath(project.directory)
|
|
219
|
+
def tag = pipelineControls.isAlphaVersion(packageJsonPath) ? 'alpha' : 'latest'
|
|
220
|
+
def publishStatus = publishToPublicRegistry(project.directory, tag)
|
|
221
|
+
|
|
222
|
+
if (publishStatus == 0) {
|
|
223
|
+
echo "${nameVersion} was published"
|
|
224
|
+
notifications.sendSlackPublishedMessage(nameVersion, SLACK_DEPLOY_CHANNEL)
|
|
225
|
+
} else {
|
|
226
|
+
notifications.sendSlackFailedToPublishMessage(nameVersion, SLACK_DEPLOY_CHANNEL)
|
|
227
|
+
|
|
228
|
+
def failureMessage = "Unable to publish ${nameVersion}"
|
|
229
|
+
echo failureMessage
|
|
230
|
+
if (isManualDeployJob()) {
|
|
231
|
+
throw new Error(failureMessage)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
def publishToPublicRegistry(directory, tag) {
|
|
237
|
+
def publishStatus = 0
|
|
238
|
+
def configuration = [vaultCredentialId: 'INT_NPM_PUBLISH_TOKEN']
|
|
239
|
+
def secrets = [
|
|
240
|
+
[path: 'secret/dev-platform/teams/appsdk/npm-public-registry', secretValues: [
|
|
241
|
+
[envVar: 'INT_NPM_TOKEN', vaultKey: 'int_npm_publish_token'],
|
|
242
|
+
]],
|
|
243
|
+
]
|
|
244
|
+
withVault([configuration: configuration, vaultSecrets: secrets]) {
|
|
245
|
+
publishStatus = sh(returnStatus: true, script: 'INT_NPM_TOKEN=$INT_NPM_TOKEN ' + "DIRECTORY=${directory} TAG=${tag} ${MAKE_PUBLISH_EXTERNAL}")
|
|
246
|
+
}
|
|
247
|
+
return publishStatus
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
def isManualPCTicketJob() {
|
|
251
|
+
return pipelineControls.isManual() && JOB_PARAMETER_RELEASE_STEP == JOB_PARAMETER_RELEASE_STEP_PC_TICKETS
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
def isManualDeployJob() {
|
|
255
|
+
return pipelineControls.isManual() && JOB_PARAMETER_RELEASE_STEP == JOB_PARAMETER_RELEASE_STEP_PUBLISH
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
def isDeploymentJob(jobParameterProject, directory) {
|
|
259
|
+
def packageJsonPath = getPackageJsonPath(directory)
|
|
260
|
+
if (isManualDeployJob()) {
|
|
261
|
+
return JOB_PARAMETER_PROJECT == jobParameterProject
|
|
262
|
+
}
|
|
263
|
+
return pipelineControls.isAlphaRelease(packageJsonPath, MAIN_BRANCH)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
def getPackageJsonPath(directory) {
|
|
267
|
+
def rootDir = pwd()
|
|
268
|
+
return "${rootDir}/${directory}/package.json"
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
def getProjectNameAndVersion(name, directory) {
|
|
272
|
+
def version = getPackageVersion(directory)
|
|
273
|
+
return "${name}@${version}"
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
def getPackageVersion(directory) {
|
|
277
|
+
def packageJsonPath = getPackageJsonPath(directory)
|
|
278
|
+
return pipelineControls.parseVersionNumber(packageJsonPath)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
def hasVersionBeenPublished(project) {
|
|
282
|
+
def version = getPackageVersion(project.directory)
|
|
283
|
+
return pipelineControls.hasPackageBeenPreviouslyPublished(project.name, version, PUBLIC_REGISTRY)
|
|
284
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import groovy.json.JsonOutput
|
|
2
|
+
import groovy.json.JsonSlurper
|
|
3
|
+
|
|
4
|
+
// JIRA ID's
|
|
5
|
+
CHANGE_TYPE_FIELD_ID = 'customfield_17900'
|
|
6
|
+
SERVICE_TEAM_FIELD_ID = 'customfield_12900'
|
|
7
|
+
RISK_FIELD_ID = 'customfield_14500'
|
|
8
|
+
IMPACT_FIELD_ID = 'customfield_15200'
|
|
9
|
+
DILIGENCE_FIELD_ID = 'customfield_15201'
|
|
10
|
+
DATA_CENTER_FIELD_ID = 'customfield_10400'
|
|
11
|
+
RUNBOOK_FIELD_ID = 'customfield_15202'
|
|
12
|
+
ROLLBACK_PLAN_FIELD_ID = 'customfield_15203'
|
|
13
|
+
PROD_CHANGE_CHECKLIST_FIELD_ID = 'customfield_11503'
|
|
14
|
+
|
|
15
|
+
STANDARD_DEPLOYMENT_ID = '16503'
|
|
16
|
+
LOW_RISK_ID = '13602'
|
|
17
|
+
LOW_IMPACT_ID = '14302'
|
|
18
|
+
LOW_DILIGENCE_ID = '14305'
|
|
19
|
+
STATIC_ASSETS_ID = '18113'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def createPCTicket(nameVersion, serviceTeamId, runBookUrl, rollbackUrl, labels) {
|
|
23
|
+
data = [
|
|
24
|
+
fields: [
|
|
25
|
+
labels: labels,
|
|
26
|
+
project: [key: 'PC'],
|
|
27
|
+
issuetype: [id: '10000'],
|
|
28
|
+
summary: "Publish ${nameVersion} to production",
|
|
29
|
+
description: "Publishing ${nameVersion} to production",
|
|
30
|
+
(SERVICE_TEAM_FIELD_ID): [id: serviceTeamId],
|
|
31
|
+
(RUNBOOK_FIELD_ID): runBookUrl,
|
|
32
|
+
(ROLLBACK_PLAN_FIELD_ID): rollbackUrl,
|
|
33
|
+
(CHANGE_TYPE_FIELD_ID): [id: STANDARD_DEPLOYMENT_ID],
|
|
34
|
+
(RISK_FIELD_ID): [id: LOW_RISK_ID],
|
|
35
|
+
(IMPACT_FIELD_ID): [id: LOW_IMPACT_ID],
|
|
36
|
+
(DILIGENCE_FIELD_ID): [id: LOW_DILIGENCE_ID],
|
|
37
|
+
(DATA_CENTER_FIELD_ID): [[id: STATIC_ASSETS_ID]],
|
|
38
|
+
(PROD_CHANGE_CHECKLIST_FIELD_ID): """
|
|
39
|
+
What: ${nameVersion}
|
|
40
|
+
When: Now
|
|
41
|
+
Why: New version
|
|
42
|
+
Risk: low
|
|
43
|
+
Customer Impact: low
|
|
44
|
+
Code Reviewer: appsdk
|
|
45
|
+
Who will be reviewing the change as it is conducted? <FILL OUT>
|
|
46
|
+
What have you done to test this change? manually tested the corresponding alpha release
|
|
47
|
+
What will you do to validate this change? manually test the newly released version
|
|
48
|
+
"""
|
|
49
|
+
]
|
|
50
|
+
]
|
|
51
|
+
issueId = callJira(data)
|
|
52
|
+
issueUrl = "https://qualtrics.atlassian.net/browse/${issueId}"
|
|
53
|
+
return issueUrl
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def callJira(data) {
|
|
57
|
+
def body = JsonOutput.toJson(data)
|
|
58
|
+
def escapedBody = body.replaceAll("'", "'\"'\"'")
|
|
59
|
+
def configuration = [$class: 'VaultConfiguration', vaultCredentialId: 'INTEGRATIONS-EXTENSION-JENKINS_VAULT-TOKEN']
|
|
60
|
+
def secrets = [[$class: 'VaultSecret', path: 'secret/external-apis/jira/it-jira-admin/prod', secretValues: [
|
|
61
|
+
[envVar: 'JIRA_API_TOKEN', vaultKey: 'JIRA_API_TOKEN'],
|
|
62
|
+
[envVar: 'JIRA_API_EMAIL_ADDRESS', vaultKey: 'JIRA_API_EMAIL_ADDRESS']
|
|
63
|
+
]]]
|
|
64
|
+
wrap([$class: 'VaultBuildWrapper', configuration: configuration, vaultSecrets: secrets]) {
|
|
65
|
+
response = sh(
|
|
66
|
+
script: """
|
|
67
|
+
curl -s -X 'POST' \
|
|
68
|
+
-H 'Content-Type: application/json' \
|
|
69
|
+
-u '${JIRA_API_EMAIL_ADDRESS}:${JIRA_API_TOKEN}' \
|
|
70
|
+
-d '${escapedBody}' \
|
|
71
|
+
'https://qualtrics.atlassian.net/rest/api/2/issue'
|
|
72
|
+
""",
|
|
73
|
+
returnStdout: true
|
|
74
|
+
).trim()
|
|
75
|
+
}
|
|
76
|
+
if (response == '') {
|
|
77
|
+
return []
|
|
78
|
+
}
|
|
79
|
+
echo response
|
|
80
|
+
def parsedResponse = new JsonSlurper().parseText(response)
|
|
81
|
+
return parsedResponse.key
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return this
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
def sendSlackPublishedMessage(projectName, channel) {
|
|
2
|
+
def message = ":rocket: <${BUILD_URL}| Published> *${projectName}*"
|
|
3
|
+
slackSend color: 'good', message: message, channel: channel
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
def sendSlackFailedToPublishMessage(projectName, channel) {
|
|
7
|
+
def message = ":boom: Failed to <${BUILD_URL}| publish> *${projectName}*"
|
|
8
|
+
slackSend color: 'warning', message: message, channel: channel
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def sendSlackVerifiedProjectMessage(projectName, channel) {
|
|
12
|
+
def message = ":four_leaf_clover: *${projectName}* <${BUILD_URL}| passed> validation"
|
|
13
|
+
slackSend color: 'good', message: message, channel: channel
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def sendSlackFailedToVerifyProjectMessage(projectName, channel) {
|
|
17
|
+
def message = ":hot_pepper: *${projectName}* <${BUILD_URL}| failed> validation"
|
|
18
|
+
slackSend color: 'warning', message: message, channel: channel
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def sendPCTicketCreatedMessage(pcTicketUrl, projectName, channel) {
|
|
22
|
+
def message = ":mailbox: <${pcTicketUrl}| PC ticket> for ${projectName} created"
|
|
23
|
+
slackSend color: 'good', message: message, channel: channel
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def setCommitStatus(status) {
|
|
27
|
+
try {
|
|
28
|
+
updateGitlabCommitStatus name: JOB_NAME, state: status
|
|
29
|
+
} catch (Exception ex) {
|
|
30
|
+
echo "Ignoring Gitlab commit status error ${ex}"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return this
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import groovy.json.JsonSlurper
|
|
2
|
+
|
|
3
|
+
PUSH = 'PUSH'
|
|
4
|
+
NOTE = 'NOTE'
|
|
5
|
+
|
|
6
|
+
def getJobDisplayName(currentBuildNumber, projectName, mainBranch) {
|
|
7
|
+
if (isMergeRequest(mainBranch)) {
|
|
8
|
+
def commitHash = sh(returnStdout: true, script: 'git rev-parse --short HEAD | tr -d \'[:space:]\'')
|
|
9
|
+
return "#${currentBuildNumber} - MR Validation ${commitHash}"
|
|
10
|
+
} else {
|
|
11
|
+
return "#${currentBuild.number} - Publishing ${projectName}"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def parseVersionNumber(packageJsonPath) {
|
|
16
|
+
def versionNumberPattern = /(\d+).(\d+).(\d+(-alpha.(\d)+)?)/
|
|
17
|
+
def manifest = readFile(packageJsonPath)
|
|
18
|
+
def version = (manifest =~ /\"version\": \"${versionNumberPattern}\"/)
|
|
19
|
+
version = (version[0][0] =~ /${versionNumberPattern}/)
|
|
20
|
+
return version[0][0]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def isAlphaVersion(packageJsonPath) {
|
|
24
|
+
def folderVerionNumber = parseVersionNumber(packageJsonPath)
|
|
25
|
+
return folderVerionNumber.contains('-alpha')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def isMergeRequest(mainBranch) {
|
|
29
|
+
return (env.gitlabActionType == PUSH || env.gitlabActionType == NOTE) && env.gitlabSourceBranch != mainBranch
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def isPushToMain(mainBranch) {
|
|
33
|
+
return env.gitlabActionType == PUSH && env.gitlabSourceBranch == mainBranch
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def isManual() {
|
|
37
|
+
return env.gitlabSourceBranch == null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
def isAlphaRelease(packageJsonPath, mainBranch) {
|
|
41
|
+
return isPushToMain(MAIN_BRANCH) && isAlphaVersion(packageJsonPath)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def hasPackageBeenPreviouslyPublished(packageName, packageVersion, registryUrl) {
|
|
45
|
+
try {
|
|
46
|
+
def getPackageVersionScript = "curl ${registryUrl}/${packageName}/${packageVersion}"
|
|
47
|
+
def packageVersionString = sh(script: getPackageVersionScript, returnStdout: true)
|
|
48
|
+
def parsedPackageVersion = new JsonSlurper().parseText(packageVersionString)
|
|
49
|
+
return parsedPackageVersion instanceof Map && parsedPackageVersion.containsKey("name")
|
|
50
|
+
} catch(Exception e) {
|
|
51
|
+
echo e
|
|
52
|
+
error "Was unable to check if package version has been already published"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
def publishResultsToTRS(uuid, serviceTag, reportFilePath, coverageFilePath, workspacePath, team) {
|
|
2
|
+
if (!fileExists(reportFilePath)) {
|
|
3
|
+
currentBuild.result = 'FAILURE'
|
|
4
|
+
echo 'Was not able to find test result artifacts'
|
|
5
|
+
return
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (!fileExists(coverageFilePath)) {
|
|
9
|
+
currentBuild.result = 'FAILURE'
|
|
10
|
+
echo 'Was not able to find code coverage artifacts'
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
sh """
|
|
15
|
+
docker run --rm \
|
|
16
|
+
-e REPORT_CONFIG_ID=${uuid} \
|
|
17
|
+
-e TEAM_NAME_TAG="${team}" \
|
|
18
|
+
-e SERVICE_NAME_TAG="${serviceTag}" \
|
|
19
|
+
-e TEST_TYPE_TAG="unittests" \
|
|
20
|
+
-e REPORT_FILE_PATH=\"./mounted/${reportFilePath}\" \
|
|
21
|
+
-e REPORT_TYPE=JUNIT \
|
|
22
|
+
-v \"${workspacePath}/${reportFilePath}\":/home/node/mounted/${reportFilePath} \
|
|
23
|
+
-e CODE_COVERAGE_FILE_PATH=\"./mounted/${coverageFilePath}\" \
|
|
24
|
+
-e CODE_COVERAGE_REPORT_TYPE=COBERTURA \
|
|
25
|
+
-v \"${workspacePath}/${coverageFilePath}\":/home/node/mounted/${coverageFilePath} \
|
|
26
|
+
registry-app.eng.qops.net:5001/test-tools/trs-uploader
|
|
27
|
+
"""
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qualtrics/plugin-ui-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-alpha.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"module": "dist/main.es.js",
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
"purge": "echo 'hi'",
|
|
13
13
|
"deps": "npm i --no-save @qualtrics/ui-react @qualtrics/base-styles react react-dom",
|
|
14
14
|
"deps:react": "npm i --no-save react react-dom",
|
|
15
|
-
"trs-deps": "
|
|
15
|
+
"trs-deps": "npm add @qualtrics/ui-react @qualtrics/base-styles react react-dom",
|
|
16
16
|
"test": "npm run deps && jest --silent",
|
|
17
|
-
"test:trs": "
|
|
17
|
+
"test:trs": "npm run test",
|
|
18
|
+
"prerelease-alpha": "npm version prerelease --preid=alpha"
|
|
18
19
|
},
|
|
19
20
|
"publishConfig": {
|
|
20
21
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -41,10 +42,10 @@
|
|
|
41
42
|
"@types/lodash.get": "^4.4.6",
|
|
42
43
|
"@types/react": "^17.0.11",
|
|
43
44
|
"@types/react-dom": "^17.0.8",
|
|
45
|
+
"@types/uuid": "^8.3.3",
|
|
44
46
|
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
|
45
47
|
"@typescript-eslint/parser": "^4.28.0",
|
|
46
48
|
"eslint": "^7.29.0",
|
|
47
|
-
"eslint-config-qualtrics-react-js": "^1.0.0",
|
|
48
49
|
"eslint-plugin-jest": "^24.3.6",
|
|
49
50
|
"eslint-plugin-json-format": "^2.0.1",
|
|
50
51
|
"eslint-plugin-react": "^7.24.0",
|
|
@@ -76,7 +77,7 @@
|
|
|
76
77
|
},
|
|
77
78
|
"peerDependencies": {
|
|
78
79
|
"@qualtrics/base-styles": "^0.2.1",
|
|
79
|
-
"@qualtrics/ui-react": "^1.
|
|
80
|
+
"@qualtrics/ui-react": "^1.95.0",
|
|
80
81
|
"react": "^16.8.0",
|
|
81
82
|
"react-dom": "^16.8.0"
|
|
82
83
|
},
|
|
@@ -108,7 +109,7 @@
|
|
|
108
109
|
[
|
|
109
110
|
"jest-junit",
|
|
110
111
|
{
|
|
111
|
-
"outputDirectory": "
|
|
112
|
+
"outputDirectory": "coverage"
|
|
112
113
|
}
|
|
113
114
|
]
|
|
114
115
|
],
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<testsuites name="jest tests" tests="41" failures="0" errors="0" time="17.093">
|
|
3
|
+
<testsuite name="FieldItem" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="2.989" tests="2">
|
|
4
|
+
<testcase classname="FieldItem renders properly" name="FieldItem renders properly" time="0.019">
|
|
5
|
+
</testcase>
|
|
6
|
+
<testcase classname="FieldItem invokes onClick handler with expected arguments when clicked" name="FieldItem invokes onClick handler with expected arguments when clicked" time="0.048">
|
|
7
|
+
</testcase>
|
|
8
|
+
</testsuite>
|
|
9
|
+
<testsuite name="SelectItem" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="3.927" tests="1">
|
|
10
|
+
<testcase classname="SelectItem fires the onChange handler with expected args" name="SelectItem fires the onChange handler with expected args" time="0.051">
|
|
11
|
+
</testcase>
|
|
12
|
+
</testsuite>
|
|
13
|
+
<testsuite name="DynamicSearchInput" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="4.657" tests="1">
|
|
14
|
+
<testcase classname="DynamicSearchInput renders correctly" name="DynamicSearchInput renders correctly" time="0.001">
|
|
15
|
+
</testcase>
|
|
16
|
+
</testsuite>
|
|
17
|
+
<testsuite name="MultiselectDropdown" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.095" tests="3">
|
|
18
|
+
<testcase classname="MultiselectDropdown toggles dropdown when button is clicked" name="MultiselectDropdown toggles dropdown when button is clicked" time="0.228">
|
|
19
|
+
</testcase>
|
|
20
|
+
<testcase classname="MultiselectDropdown calls onChange handler when item is clicked" name="MultiselectDropdown calls onChange handler when item is clicked" time="0.186">
|
|
21
|
+
</testcase>
|
|
22
|
+
<testcase classname="MultiselectDropdown closes menu on click away" name="MultiselectDropdown closes menu on click away" time="0.051">
|
|
23
|
+
</testcase>
|
|
24
|
+
</testsuite>
|
|
25
|
+
<testsuite name="FieldRow" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.164" tests="6">
|
|
26
|
+
<testcase classname="FieldRow fires the onFieldNameChange handler when field name is changed" name="FieldRow fires the onFieldNameChange handler when field name is changed" time="0.062">
|
|
27
|
+
</testcase>
|
|
28
|
+
<testcase classname="FieldRow fires the onFieldValueChange handler when field's value is changed" name="FieldRow fires the onFieldValueChange handler when field's value is changed" time="0.031">
|
|
29
|
+
</testcase>
|
|
30
|
+
<testcase classname="FieldRow fires the onFieldSwitch handler when field is switched to different field type" name="FieldRow fires the onFieldSwitch handler when field is switched to different field type" time="0.054">
|
|
31
|
+
</testcase>
|
|
32
|
+
<testcase classname="FieldRow fires the onRemove handler when the delete button is clicked" name="FieldRow fires the onRemove handler when the delete button is clicked" time="0.026">
|
|
33
|
+
</testcase>
|
|
34
|
+
<testcase classname="FieldRow should allow for searching in field row input dropdown when searchable field provided" name="FieldRow should allow for searching in field row input dropdown when searchable field provided" time="0.205">
|
|
35
|
+
</testcase>
|
|
36
|
+
<testcase classname="FieldRow should not allow searching in field row input dropdown when searchable is false" name="FieldRow should not allow searching in field row input dropdown when searchable is false" time="0.022">
|
|
37
|
+
</testcase>
|
|
38
|
+
</testsuite>
|
|
39
|
+
<testsuite name="TaskFields" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.336" tests="4">
|
|
40
|
+
<testcase classname="TaskFields only renders custom fields if custom field configuration options provided" name="TaskFields only renders custom fields if custom field configuration options provided" time="0.441">
|
|
41
|
+
</testcase>
|
|
42
|
+
<testcase classname="TaskFields fires onFieldNameChange handler when a custom field's name is changed" name="TaskFields fires onFieldNameChange handler when a custom field's name is changed" time="0.034">
|
|
43
|
+
</testcase>
|
|
44
|
+
<testcase classname="TaskFields fires onFieldAdd handler when a field is added" name="TaskFields fires onFieldAdd handler when a field is added" time="0.056">
|
|
45
|
+
</testcase>
|
|
46
|
+
<testcase classname="TaskFields fires onCustomFieldAdd handler when a custom field is added" name="TaskFields fires onCustomFieldAdd handler when a custom field is added" time="0.047">
|
|
47
|
+
</testcase>
|
|
48
|
+
</testsuite>
|
|
49
|
+
<testsuite name="PipedTextInput" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="5.506" tests="6">
|
|
50
|
+
<testcase classname="PipedTextInput toggles menu open/closed on click" name="PipedTextInput toggles menu open/closed on click" time="0.352">
|
|
51
|
+
</testcase>
|
|
52
|
+
<testcase classname="PipedTextInput fires onChange handler when input value changes from selecting piped text" name="PipedTextInput fires onChange handler when input value changes from selecting piped text" time="0.117">
|
|
53
|
+
</testcase>
|
|
54
|
+
<testcase classname="PipedTextInput fires onChange handler when input value changes from user typing in input box" name="PipedTextInput fires onChange handler when input value changes from user typing in input box" time="0.01">
|
|
55
|
+
</testcase>
|
|
56
|
+
<testcase classname="PipedTextInput fires onSelect handler when piped text is selected" name="PipedTextInput fires onSelect handler when piped text is selected" time="0.097">
|
|
57
|
+
</testcase>
|
|
58
|
+
<testcase classname="PipedTextInput fires onClose handler when menu is closed" name="PipedTextInput fires onClose handler when menu is closed" time="0.086">
|
|
59
|
+
</testcase>
|
|
60
|
+
<testcase classname="PipedTextInput inserts piped text at the position of the cursor in the input box when piped text is selected" name="PipedTextInput inserts piped text at the position of the cursor in the input box when piped text is selected" time="0.086">
|
|
61
|
+
</testcase>
|
|
62
|
+
</testsuite>
|
|
63
|
+
<testsuite name="PipedTextSelect" errors="0" failures="0" skipped="0" timestamp="2021-11-15T23:34:01" time="13.594" tests="18">
|
|
64
|
+
<testcase classname="PipedTextSelect filters menu items to items containing the search value as text" name="PipedTextSelect filters menu items to items containing the search value as text" time="0.383">
|
|
65
|
+
</testcase>
|
|
66
|
+
<testcase classname="PipedTextSelect filters menu items to items containing the search value as a substring" name="PipedTextSelect filters menu items to items containing the search value as a substring" time="0.17">
|
|
67
|
+
</testcase>
|
|
68
|
+
<testcase classname="PipedTextSelect filters menu items with special characters to items containing the search value" name="PipedTextSelect filters menu items with special characters to items containing the search value" time="0.169">
|
|
69
|
+
</testcase>
|
|
70
|
+
<testcase classname="PipedTextSelect renders submenu correctly when hovering over a submenu item" name="PipedTextSelect renders submenu correctly when hovering over a submenu item" time="0.627">
|
|
71
|
+
</testcase>
|
|
72
|
+
<testcase classname="PipedTextSelect hides other submenus when hovering over another submenu" name="PipedTextSelect hides other submenus when hovering over another submenu" time="0.714">
|
|
73
|
+
</testcase>
|
|
74
|
+
<testcase classname="PipedTextSelect hides other submenus when hovering over another item" name="PipedTextSelect hides other submenus when hovering over another item" time="0.283">
|
|
75
|
+
</testcase>
|
|
76
|
+
<testcase classname="PipedTextSelect fires onSelect handler when a menu item is selected" name="PipedTextSelect fires onSelect handler when a menu item is selected" time="0.219">
|
|
77
|
+
</testcase>
|
|
78
|
+
<testcase classname="PipedTextSelect doesn't fire onSelect handler when a disabled menu item is selected" name="PipedTextSelect doesn't fire onSelect handler when a disabled menu item is selected" time="0.174">
|
|
79
|
+
</testcase>
|
|
80
|
+
<testcase classname="PipedTextSelect fires onClose handler when menu item selected" name="PipedTextSelect fires onClose handler when menu item selected" time="0.151">
|
|
81
|
+
</testcase>
|
|
82
|
+
<testcase classname="PipedTextSelect fires onClose handler when nested item selected" name="PipedTextSelect fires onClose handler when nested item selected" time="1.345">
|
|
83
|
+
</testcase>
|
|
84
|
+
<testcase classname="PipedTextSelect fires onClose handler when clicking outside of the menu" name="PipedTextSelect fires onClose handler when clicking outside of the menu" time="0.044">
|
|
85
|
+
</testcase>
|
|
86
|
+
<testcase classname="PipedTextSelect displays the custom input message if none of the menu items match the search query and custom input is enabled" name="PipedTextSelect displays the custom input message if none of the menu items match the search query and custom input is enabled" time="0.578">
|
|
87
|
+
</testcase>
|
|
88
|
+
<testcase classname="PipedTextSelect correctly creates a custom locator based on the provided scheme when the user hits the "enter" in the search query" name="PipedTextSelect correctly creates a custom locator based on the provided scheme when the user hits the "enter" in the search query" time="0.558">
|
|
89
|
+
</testcase>
|
|
90
|
+
<testcase classname="PipedTextSelect calls the onClose handler when custom input is enabled and the user hits "enter" in the search query" name="PipedTextSelect calls the onClose handler when custom input is enabled and the user hits "enter" in the search query" time="0.527">
|
|
91
|
+
</testcase>
|
|
92
|
+
<testcase classname="PipedTextSelect does not call the onClose handler when custom input is disabled and the user hits "enter" in the search query" name="PipedTextSelect does not call the onClose handler when custom input is disabled and the user hits "enter" in the search query" time="0.704">
|
|
93
|
+
</testcase>
|
|
94
|
+
<testcase classname="PipedTextSelect displays piped text items provided by an async "items" function" name="PipedTextSelect displays piped text items provided by an async "items" function" time="0.635">
|
|
95
|
+
</testcase>
|
|
96
|
+
<testcase classname="PipedTextSelect displays piped text items provided by a non-async "items" function" name="PipedTextSelect displays piped text items provided by a non-async "items" function" time="0.588">
|
|
97
|
+
</testcase>
|
|
98
|
+
<testcase classname="PipedTextSelect displays loading error message if async piped text items function fails to resolve" name="PipedTextSelect displays loading error message if async piped text items function fails to resolve" time="0.615">
|
|
99
|
+
</testcase>
|
|
100
|
+
</testsuite>
|
|
101
|
+
</testsuites>
|
package/.bee
DELETED