Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Melodic
melodic-frontend
Commits
12eba0bf
Commit
12eba0bf
authored
Jul 27, 2020
by
Maria C
Browse files
Merge branch 'ftt' into 'rc3.1'
serverless testing: improved UI See merge request
!6
parents
850cff34
823f3178
Pipeline
#9209
passed with stage
in 7 minutes and 40 seconds
Changes
11
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
src/app/serverless-testing/model/TestConfigurationResponse.ts
0 → 100644
View file @
12eba0bf
export
class
TestConfigurationResponse
{
path
:
string
;
configuration
:
TestConfiguration
;
}
class
TestConfiguration
{
tests
:
Array
<
FunctionTestConfiguration
>
;
}
class
FunctionTestConfiguration
{
functionName
:
string
;
triggerPath
:
string
;
testCases
:
Array
<
TestCaseConfiguration
>
;
}
class
TestCaseConfiguration
{
event
:
string
;
condition
:
string
;
expectedValue
:
string
;
}
src/app/serverless-testing/model/TestResultTree.ts
View file @
12eba0bf
class
TestCaseResult
{
event
:
string
;
expectedOutput
:
string
;
condition
:
string
;
expectedValue
:
string
;
actualOutput
:
string
;
message
:
string
;
result
:
string
;
...
...
src/app/serverless-testing/model/UploadYmlResponse.ts
deleted
100644 → 0
View file @
850cff34
export
class
UploadYmlResponse
{
testConfigfilePath
:
string
;
}
src/app/serverless-testing/service/serverless-testing.service.ts
View file @
12eba0bf
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
HttpClient
,
HttpHeaders
}
from
'
@angular/common/http
'
;
import
{
HttpClient
}
from
'
@angular/common/http
'
;
import
{
tap
}
from
'
rxjs/operators
'
;
import
{
Observable
}
from
'
rxjs
'
;
import
{
AppConfigService
}
from
'
../../app-config/service/app-config.service
'
;
import
{
UploadYmlResponse
}
from
'
../model/UploadYmlResponse
'
;
import
{
TestResultTree
}
from
'
../model/TestResultTree
'
;
import
{
TestConfigurationResponse
}
from
'
../model/TestConfigurationResponse
'
;
@
Injectable
({
providedIn
:
'
root
'
})
export
class
ServerlessTestingService
{
apiUrl
=
`
${
AppConfigService
.
settings
.
apiUrl
}
/auth/test`
;
testConfigUrl
=
this
.
apiUrl
+
'
/config
'
;
constructor
(
private
http
:
HttpClient
)
{
}
uploadTestConfiguration
(
formData
:
FormData
):
Observable
<
UploadYmlResponse
>
{
const
uploadUrl
=
this
.
apiUrl
+
'
/upload
'
;
return
this
.
http
.
post
(
uploadUrl
,
formData
,
{
responseType
:
'
json
'
}).
pipe
(
tap
(
(
response
:
UploadYmlResponse
)
=>
{
console
.
log
(
'
Response:
'
,
response
);
},
uploadTestConfiguration
(
formData
:
FormData
):
Observable
<
TestConfigurationResponse
>
{
return
this
.
http
.
post
(
this
.
testConfigUrl
,
formData
,
{
responseType
:
'
json
'
}).
pipe
(
tap
(
(
response
:
TestConfigurationResponse
)
=>
{
console
.
log
(
'
Response:
'
,
response
);
},
error
=>
{
console
.
log
(
'
Error while uploading test configuration file:
'
,
error
);
}
));
}
getTestConfiguration
():
Observable
<
TestConfigurationResponse
>
{
return
this
.
http
.
get
(
this
.
testConfigUrl
,
{
responseType
:
'
json
'
}).
pipe
(
tap
(
(
response
:
TestConfigurationResponse
)
=>
{
console
.
log
(
'
Response:
'
,
response
);
},
error
=>
{
console
.
log
(
'
Error while getting test configuration file:
'
,
error
);
}
));
}
deleteTestConfiguration
():
Observable
<
any
>
{
return
this
.
http
.
delete
(
this
.
testConfigUrl
,
{
responseType
:
'
json
'
}).
pipe
(
tap
(
()
=>
{
console
.
log
(
'
Deleted test config file successfully.
'
);
},
error
=>
{
console
.
log
(
'
Error while deleting test configuration file:
'
,
error
);
}
));
}
runTests
():
Observable
<
TestResultTree
>
{
const
requestUrl
=
this
.
apiUrl
+
'
/run
'
;
return
this
.
http
.
post
(
requestUrl
,
null
,
{
responseType
:
'
json
'
}).
pipe
(
tap
(
...
...
src/app/serverless-testing/test-results/test-results.component.css
View file @
12eba0bf
div
{
padding-bottom
:
10px
;
samp
{
white-space
:
pre-wrap
;
}
pre
{
white-space
:
pre-wrap
;
td
{
padding
:
5px
30px
5px
0
;
vertical-align
:
top
;
}
mat-card
{
margin
:
10px
0
;
}
.mat-card-content
{
margin-left
:
16px
;
}
.test-case-key
{
font-weight
:
bold
;
}
.mat-expansion-panel-header-title
{
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
src/app/serverless-testing/test-results/test-results.component.html
View file @
12eba0bf
...
...
@@ -15,7 +15,7 @@
<span>
Message
<pre>
{{ results.message }}
</pre></span>
</div>
<ng-template
#resultTree
>
<
div
>
Overall duration: {{ results.duration }} seconds
</
div
>
<
p
>
Overall duration: {{ results.duration }} seconds
</
p
>
<mat-accordion>
<mat-expansion-panel
*ngFor=
"let functionTestResult of results.functionTestResults"
...
...
@@ -23,22 +23,26 @@
(closed)=
"panelOpenState = false"
>
<mat-expansion-panel-header>
<mat-panel-title>
<span>
{{ functionTestResult.functionName }}
(
{{ functionTestResult.overallResult }})
</span>
<span>
<samp>
{{ functionTestResult.functionName }}
</samp>
(
<samp>
{{ functionTestResult.overallResult }}
</samp>
)
</span>
</mat-panel-title>
</mat-expansion-panel-header>
<span>
Function name
<pre>
{{ functionTestResult.functionName }}
</pre></span>
<span>
Tests duration (seconds)
<pre>
{{ functionTestResult.duration }}
</pre></span>
<span>
Summary
<pre>
{{ functionTestResult.overallResult }}
</pre></span>
<span>
Number of passed tests
<pre>
{{ functionTestResult.passed }}
</pre></span>
<span>
Number of failed tests
<pre>
{{ functionTestResult.failed }}
</pre></span>
<span>
Number of ignored tests
<pre>
{{ functionTestResult.ignored }}
</pre></span>
<span
*ngIf=
"functionTestResult.failedAtStage"
>
Failed to run tests due to failure at stage
<pre>
{{ functionTestResult.failedAtStage }}
</pre>
</span>
<span
*ngIf=
"functionTestResult.message"
>
Message
<pre>
{{ functionTestResult.message }}
</pre></span>
<table>
<tr><td
class=
"test-case-key"
>
Function name
</td><td><samp>
{{ functionTestResult.functionName }}
</samp></td></tr>
<tr><td
class=
"test-case-key"
>
Tests duration
</td><td>
{{ functionTestResult.duration }} seconds
</td></tr>
<tr><td
class=
"test-case-key"
>
Number of passed tests
</td><td>
{{ functionTestResult.passed }}
</td></tr>
<tr><td
class=
"test-case-key"
>
Number of failed tests
</td><td>
{{ functionTestResult.failed }}
</td></tr>
<tr><td
class=
"test-case-key"
>
Number of passed ignored
</td><td>
{{ functionTestResult.ignored }}
</td></tr>
<tr
*ngIf=
"functionTestResult.failedAtStage"
>
<td
class=
"test-case-key"
>
Failed to run tests due to failure at stage
</td>
<td><samp>
{{ functionTestResult.failedAtStage }}
</samp></td>
</tr>
<tr
*ngIf=
"functionTestResult.message"
>
<td
class=
"test-case-key"
>
Message
</td>
<td><samp>
{{ functionTestResult.message }}
</samp></td>
</tr>
</table>
<mat-accordion>
<
div
>
Test cases
</
div
>
<
h4
>
Test cases
</
h4
>
<mat-expansion-panel
*ngFor=
"let testCaseResult of functionTestResult.testCaseResults; let i = index"
(opened)=
"panelOpenState = true"
...
...
@@ -49,12 +53,20 @@
<span>
#{{ i+1 }} ({{ testCaseResult.result }})
</span>
</mat-panel-title>
</mat-expansion-panel-header>
<span>
Result
<pre>
{{ testCaseResult.result }}
</pre></span>
<span>
Duration
<pre>
{{ testCaseResult.duration }}
</pre></span>
<span>
Input
<pre>
{{ testCaseResult.event }}
</pre></span>
<span>
Expected output
<pre>
{{ testCaseResult.expectedOutput }}
</pre></span>
<span
*ngIf=
"testCaseResult.actualOutput"
>
Actual output
<pre>
{{ testCaseResult.actualOutput }}
</pre></span>
<span
*ngIf=
"testCaseResult.message"
>
Message
<pre>
{{ testCaseResult.message }}
</pre></span>
<table>
<tr><td
class=
"test-case-key"
>
Result
</td><td><samp>
{{ testCaseResult.result }}
</samp></td></tr>
<tr><td
class=
"test-case-key"
>
Duration
</td><td>
{{ testCaseResult.duration }} seconds
</td></tr>
<tr><td
class=
"test-case-key"
>
Input event
</td><td><samp>
{{ testCaseResult.event }}
</samp></td></tr>
<tr><td
class=
"test-case-key"
>
Condition for the output
</td><td><samp>
{{ testCaseResult.condition }}
</samp></td></tr>
<tr><td
class=
"test-case-key"
>
Expected value
</td><td><samp>
{{ testCaseResult.expectedValue }}
</samp></td></tr>
<tr
*ngIf=
"testCaseResult.actualOutput"
>
<td
class=
"test-case-key"
>
Actual output
</td><td><samp>
{{ testCaseResult.actualOutput }}
</samp></td>
</tr>
<tr
*ngIf=
"testCaseResult.message"
>
<td
class=
"test-case-key"
>
Message
</td><td><samp>
{{ testCaseResult.message }}
</samp></td>
</tr>
</table>
</mat-expansion-panel>
</mat-accordion>
...
...
src/app/serverless-testing/test-runner/test-runner.component.css
View file @
12eba0bf
div
{
margin-bottom
:
10px
;
margin-top
:
10px
;
button
{
padding
:
0
5px
;
}
button
{
margin-left
:
var
(
--double-margin
);
mat-card
{
margin
:
10px
0
;
}
.mat-card-content
{
margin-left
:
16px
;
}
.mat-card-actions
{
margin-left
:
16px
;
}
.mat-card-actions
span
{
padding-right
:
8px
;
}
src/app/serverless-testing/test-runner/test-runner.component.html
View file @
12eba0bf
...
...
@@ -4,14 +4,11 @@
<mat-card-title>
Run tests
</mat-card-title>
<mat-card-subtitle>
Click on the button to execute the tests.
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
Click here to run the tests.
</div>
<button
mat-fab
(click)=
"runTests()"
>
<mat-icon>
play_arrow
</mat-icon>
</button>
<div
*ngIf=
"testsRunning"
>
<div>
Please wait. It may take a while...
...
...
@@ -19,5 +16,17 @@
<mat-progress-bar
color=
"primary"
mode=
"indeterminate"
></mat-progress-bar>
</div>
</mat-card-content>
<mat-card-actions>
<button
mat-raised-button
color=
"primary"
[disabled]=
"testsRunning"
(click)=
"runTests()"
title=
"Run tests"
>
<mat-icon>
play_arrow
</mat-icon>
<span>
Run tests
</span>
</button>
</mat-card-actions>
</mat-card>
</div>
src/app/serverless-testing/uploader-yml/uploader-yml.component.css
View file @
12eba0bf
td
{
padding
:
5px
30px
5px
0
;
vertical-align
:
top
;
}
button
{
padding
:
0
5px
;
}
mat-card
{
margin
:
10px
0
;
}
.mat-button
{
margin-left
:
16px
;
}
.file
{
display
:
flex
;
flex-wrap
:
wrap
;
align-items
:
center
;
margin-top
:
20px
;
}
.mat-accordion
.mat-expansion-panel
:first-of-type
{
margin-top
:
10px
;
}
.mat-expansion-panel-header-title
{
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
.test-case-key
{
font-weight
:
bold
;
}
.delete-button
{
color
:
var
(
--color-warn
);
}
.mat-card-content
{
margin-left
:
16px
;
}
.mat-card-actions
{
margin-left
:
16px
;
}
.action-buttons
{
display
:
flex
;
flex-wrap
:
wrap
;
.mat-card-actions
span
{
padding-right
:
8px
;
}
src/app/serverless-testing/uploader-yml/uploader-yml.component.html
View file @
12eba0bf
...
...
@@ -21,28 +21,105 @@
</div>
<div
class=
"file"
*ngIf=
"fileToUpload != null"
>
{{fileToUpload.name}} ({{convertBytesToSize(fileToUpload.size)}})
</div>
</mat-card-content>
<mat-card-actions>
<button
mat-button
color=
"primary"
[disabled]=
"fileUploadDisabled"
(click)=
"onFileUploadEvent()"
title=
"Upload file"
>
<mat-icon>
file_upload
</mat-icon>
<span>
Upload
</span>
</button>
<button
mat-button
color=
"warn"
[disabled]=
"fileUploadDisabled"
(click)=
"removeSelectedFile()"
title=
"Cancel"
>
<mat-icon>
cancel
</mat-icon>
<span>
Cancel
</span>
</button>
</mat-card-actions>
</mat-card>
<mat-card>
<mat-card-header>
<mat-card-title>
Test configuration
</mat-card-title>
<mat-card-subtitle>
You can check the current test configuration.
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<div
*ngIf=
"!testConfig; else testConfigBlock"
>
<p>
There is no test configuration file on the server.
</p>
</div>
<ng-template
#testConfigBlock
>
<h4>
Configuration file
</h4>
<pre>
{{ testConfig.path }}
</pre>
<h4>
Tests
</h4>
<mat-accordion>
<mat-expansion-panel
*ngFor=
"let functionConfig of testConfig.configuration.tests"
(opened)=
"panelOpenState = true"
(closed)=
"panelOpenState = false"
>
<mat-expansion-panel-header>
<mat-panel-title>
<samp>
{{ functionConfig.functionName }}
</samp>
</mat-panel-title>
</mat-expansion-panel-header>
<table
*ngIf=
"functionConfig.triggerPath"
>
<tr><td
class=
"test-case-key"
>
Trigger path
</td><td><samp>
{{ functionConfig.triggerPath }}
</samp></td></tr>
</table>
<mat-accordion>
<h4>
Test cases
</h4>
<mat-expansion-panel
*ngFor=
"let testCase of functionConfig.testCases; let i = index"
(opened)=
"panelOpenState = true"
(closed)=
"panelOpenState = false"
>
<mat-expansion-panel-header>
<mat-panel-title>
<span>
#{{ i+1 }} Output invoked with
<samp>
{{ testCase.event }}
</samp>
{{ formatCondition(testCase.condition) }}
<samp>
{{ testCase.expectedValue }}
</samp>
</span>
</mat-panel-title>
</mat-expansion-panel-header>
<table>
<tr><td
class=
"test-case-key"
>
Input event
</td><td><samp>
{{ testCase.event }}
</samp></td></tr>
<tr><td
class=
"test-case-key"
>
Condition for the output
</td><td><samp>
{{ testCase.condition }}
</samp></td></tr>
<tr><td
class=
"test-case-key"
>
Expected value
</td><td><samp>
{{ testCase.expectedValue }}
</samp></td></tr>
</table>
</mat-expansion-panel>
</mat-accordion>
<div
class=
"action-buttons"
>
<button
mat-button
color=
"primary"
[disabled]=
"fileUploadDisabled"
(click)=
"onFileUploadEvent()"
title=
"Upload file"
>
<mat-icon>
file_upload
</mat-icon>
</button>
</mat-expansion-panel>
</mat-accordion>
</ng-template>
</div>
<button
mat-button
color=
"warn"
[disabled]=
"fileUploadDisabled"
(click)=
"removeSelectedFile()"
title=
"Remove selected file"
>
<mat-icon>
cancel
</mat-icon>
</button>
</div>
</div>
</mat-card-content>
<mat-card-actions>
<button
*ngIf=
"testConfig"
mat-button
class=
"delete-button"
[disabled]=
"deletingTestConfig"
(click)=
"onFileDeletionEvent()"
title=
"Delete test configuration"
>
<mat-icon>
delete
</mat-icon>
Delete
</button>
</mat-card-actions>
</mat-card>
</div>
src/app/serverless-testing/uploader-yml/uploader-yml.component.ts
View file @
12eba0bf
import
{
Component
,
ElementRef
,
Input
,
OnInit
,
ViewChild
}
from
'
@angular/core
'
;
import
{
ServerlessTestingService
}
from
'
../service/serverless-testing.service
'
;
import
{
MatSnackBar
}
from
'
@angular/material
'
;
import
{
TestConfigurationResponse
}
from
'
../model/TestConfigurationResponse
'
;
@
Component
({
selector
:
'
app-uploader-yml
'
,
...
...
@@ -9,8 +10,11 @@ import {MatSnackBar} from '@angular/material';
})
export
class
UploaderYmlComponent
implements
OnInit
{
uploadInProgress
=
false
;
panelOpenState
=
false
;
deletingTestConfig
=
false
;
fileToUpload
:
File
=
null
;
total
:
number
=
null
;
testConfig
:
TestConfigurationResponse
=
null
;
@
ViewChild
(
'
ymlFile
'
)
ymlFile
:
ElementRef
;
...
...
@@ -21,6 +25,22 @@ export class UploaderYmlComponent implements OnInit {
}
ngOnInit
():
void
{
this
.
testingServerlessService
.
getTestConfiguration
().
subscribe
(
response
=>
{
this
.
testConfig
=
response
;
},
error
=>
{
if
(
error
.
error
.
status
===
404
)
{
this
.
testConfig
=
null
;
}
else
{
this
.
snackBar
.
open
(
`Error while getting test config:
${
error
.
error
.
message
}
`
,
'
Close
'
,
{
duration
:
10000
}
);
}
}
);
}
@
Input
()
...
...
@@ -42,6 +62,23 @@ export class UploaderYmlComponent implements OnInit {
return
Math
.
round
(
bytes
/
Math
.
pow
(
1024
,
i
))
+
'
'
+
sizes
[
i
];
}
formatCondition
(
condition
):
string
{
switch
(
condition
)
{
case
'
EQUALS
'
:
return
'
should equal
'
;
case
'
STARTS_WITH
'
:
return
'
should start with
'
;
case
'
ENDS_WITH
'
:
return
'
should end with
'
;
case
'
CONTAINS_SUBSTRING
'
:
return
'
should contain substring
'
;
case
'
EQUALS_IGNORE_CASE
'
:
return
'
should equal (ignoring the case)
'
;
case
'
MATCHES_REGEX
'
:
return
'
should match regex pattern
'
;
}
}
get
fileUploadDisabled
()
{
return
this
.
uploadInProgress
||
this
.
fileToUpload
==
null
;
}
...
...
@@ -63,12 +100,17 @@ export class UploaderYmlComponent implements OnInit {
'
Close
'
,
{
duration
:
10000
},
);
this
.
testConfig
=
response
;
this
.
removeSelectedFile
();
},
error
=>
{
this
.
uploadInProgress
=
false
;
console
.
log
(
error
);
this
.
snackBar
.
open
(
`Error while uploading:
${
error
.
error
.
message
}
`
,
'
Close
'
,
{
duration
:
10000
});
this
.
snackBar
.
open
(
`Error while uploading:
${
error
.
error
.
message
}
`
,
'
Close
'
,
{
duration
:
10000
}
);
}
);
}
...
...
@@ -77,4 +119,28 @@ export class UploaderYmlComponent implements OnInit {
this
.
fileToUpload
=
null
;
this
.
ymlFile
.
nativeElement
.
value
=
null
;
}
onFileDeletionEvent
()
{
this
.
deletingTestConfig
=
true
;
this
.
testingServerlessService
.
deleteTestConfiguration
().
subscribe
(
response
=>
{
this
.
deletingTestConfig
=
false
;
this
.
testConfig
=
null
;
this
.
snackBar
.
open
(
'
Successfully deleted test config file.
'
,
'
Close
'
,
{
duration
:
10000
},
);
},
error
=>
{
this
.
deletingTestConfig
=
false
;
console
.
log
(
error
);
this
.
snackBar
.
open
(
`Error while deleting test config file:
${
error
.
error
.
message
}
`
,
'
Close
'
,
{
duration
:
10000
}
);
}
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment