Skip to content

Commit 01130a7

Browse files
committed
added action to upload files more conveniently
1 parent bd6fbb4 commit 01130a7

File tree

11 files changed

+236
-2
lines changed

11 files changed

+236
-2
lines changed

backend/src/main/java/de/learnlib/alex/data/entities/SymbolAction.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import de.learnlib.alex.data.entities.actions.web.SubmitAction;
6565
import de.learnlib.alex.data.entities.actions.web.SwitchTo;
6666
import de.learnlib.alex.data.entities.actions.web.SwitchToFrame;
67+
import de.learnlib.alex.data.entities.actions.web.UploadFileAction;
6768
import de.learnlib.alex.data.entities.actions.web.WaitForNodeAction;
6869
import de.learnlib.alex.data.entities.actions.web.WaitForNodeAttributeAction;
6970
import de.learnlib.alex.data.entities.actions.web.WaitForTextAction;
@@ -138,6 +139,7 @@
138139
@JsonSubTypes.Type(name = "web_select", value = SelectAction.class),
139140
@JsonSubTypes.Type(name = "web_switchTo", value = SwitchTo.class),
140141
@JsonSubTypes.Type(name = "web_switchToFrame", value = SwitchToFrame.class),
142+
@JsonSubTypes.Type(name = "web_uploadFile", value = UploadFileAction.class),
141143
@JsonSubTypes.Type(name = "web_waitForTitle", value = WaitForTitleAction.class),
142144
@JsonSubTypes.Type(name = "web_waitForNode", value = WaitForNodeAction.class),
143145
@JsonSubTypes.Type(name = "web_waitForNodeAttribute", value = WaitForNodeAttributeAction.class),
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright 2018 TU Dortmund
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.learnlib.alex.data.entities.actions.web;
18+
19+
import com.fasterxml.jackson.annotation.JsonTypeName;
20+
import de.learnlib.alex.common.utils.LoggerMarkers;
21+
import de.learnlib.alex.data.entities.ExecuteResult;
22+
import de.learnlib.alex.data.entities.SymbolAction;
23+
import de.learnlib.alex.data.entities.WebElementLocator;
24+
import de.learnlib.alex.learning.services.connectors.ConnectorManager;
25+
import de.learnlib.alex.learning.services.connectors.FileStoreConnector;
26+
import de.learnlib.alex.learning.services.connectors.WebSiteConnector;
27+
import org.apache.logging.log4j.LogManager;
28+
import org.apache.logging.log4j.Logger;
29+
import org.hibernate.validator.constraints.NotEmpty;
30+
import org.openqa.selenium.NoSuchElementException;
31+
import org.openqa.selenium.WebElement;
32+
33+
import javax.persistence.DiscriminatorValue;
34+
import javax.persistence.Embedded;
35+
import javax.persistence.Entity;
36+
import javax.validation.constraints.NotNull;
37+
import java.io.Serializable;
38+
39+
/** Action to upload a file into an input[type="file"] element. */
40+
@Entity
41+
@DiscriminatorValue("web_uploadFile")
42+
@JsonTypeName("web_uploadFile")
43+
public class UploadFileAction extends SymbolAction implements Serializable {
44+
45+
private static final long serialVersionUID = -1094366952752580170L;
46+
47+
private static final Logger LOGGER = LogManager.getLogger();
48+
49+
/** The input element to upload the file to. */
50+
@NotNull
51+
@Embedded
52+
private WebElementLocator node;
53+
54+
/** The name of the uploaded file in ALEX. */
55+
@NotEmpty
56+
private String fileName;
57+
58+
@Override
59+
protected ExecuteResult execute(ConnectorManager connector) {
60+
final FileStoreConnector fileStore = connector.getConnector(FileStoreConnector.class);
61+
final WebSiteConnector webSiteConnector = connector.getConnector(WebSiteConnector.class);
62+
63+
final WebElementLocator nodeWithVariables =
64+
new WebElementLocator(insertVariableValues(node.getSelector()), node.getType());
65+
66+
try {
67+
final String path = fileStore.getAbsoluteFileLocation(symbol.getProjectId(), fileName);
68+
final WebElement el = webSiteConnector.getElement(nodeWithVariables);
69+
70+
if (el.getTagName().equals("input") && el.getAttribute("type").equals("file")) {
71+
el.sendKeys(path);
72+
return getSuccessOutput();
73+
} else {
74+
throw new NoSuchElementException("The element is not an input file element.");
75+
}
76+
} catch (IllegalStateException e) {
77+
LOGGER.info(LoggerMarkers.LEARNER, "The file '{}' could not be found in ALEX.", this.fileName);
78+
return getFailedOutput();
79+
} catch (NoSuchElementException e) {
80+
LOGGER.info(LoggerMarkers.LEARNER, "The element could not be found or is not an input element.");
81+
return getFailedOutput();
82+
} catch (Exception e) {
83+
LOGGER.info(LoggerMarkers.LEARNER, "The file could not be uploaded for an unknown reason", e);
84+
return getFailedOutput();
85+
}
86+
}
87+
88+
public WebElementLocator getNode() {
89+
return node;
90+
}
91+
92+
public void setNode(WebElementLocator node) {
93+
this.node = node;
94+
}
95+
96+
public String getFileName() {
97+
return fileName;
98+
}
99+
100+
public void setFileName(String fileName) {
101+
this.fileName = fileName;
102+
}
103+
}

frontend/src/main/javascript/src/js/components/forms/actions/action-form/action-form.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<check-node-attribute-value-action-form action="vm.action" ng-switch-when="web_checkNodeAttributeValue"></check-node-attribute-value-action-form>
2929
<switch-to-action-form action="vm.action" ng-switch-when="web_switchTo"></switch-to-action-form>
3030
<switch-to-frame-action-form action="vm.action" ng-switch-when="web_switchToFrame"></switch-to-frame-action-form>
31+
<upload-file-action-form action="vm.action" ng-switch-when="web_uploadFile"></upload-file-action-form>
3132

3233
<!-- rest actions -->
3334
<request-action-form action="vm.action" ng-switch-when="rest_call"></request-action-form>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<h4><strong>Upload file</strong></h4>
2+
3+
<p class="text-muted">
4+
Uploads a file to an <strong>input[type="file"]</strong> element.
5+
</p>
6+
<hr>
7+
8+
<div class="alert alert-info" ng-if="vm.files.length === 0">
9+
There are not uploaded files in ALEX yet.
10+
</div>
11+
12+
<div ng-if="vm.files.length > 0">
13+
<node-form-group node="vm.action.node"></node-form-group>
14+
15+
<div class="form-group">
16+
<label>File to upload</label>
17+
<select class="form-control" ng-model="vm.action.fileName">
18+
<option selected value="">Select a file to upload</option>
19+
<option ng-repeat="file in vm.files" value="{{file.name}}">{{file.name}}</option>
20+
</select>
21+
</div>
22+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2018 TU Dortmund
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export const uploadFileActionFormComponent = {
18+
template: require('./upload-file-action-form.component.html'),
19+
bindings: {
20+
action: '='
21+
},
22+
controllerAs: 'vm',
23+
controller: class UploadFileActionFormComponent {
24+
25+
/**
26+
* Constructor.
27+
*
28+
* @param {FileResource} FileResource
29+
* @param {ProjectService} ProjectService
30+
*/
31+
// @ngInject
32+
constructor(FileResource, ProjectService) {
33+
this.fileResource = FileResource;
34+
this.projectService = ProjectService;
35+
36+
this.files = [];
37+
38+
this.fileResource.getAll(this.project.id).then(files => this.files = files);
39+
}
40+
41+
get project() {
42+
return this.projectService.store.currentProject;
43+
}
44+
}
45+
};

frontend/src/main/javascript/src/js/components/modals/action-create-modal/action-create-modal.component.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@ export class ActionCreateModalComponent {
7171
{type: actionType.WEB_SUBMIT, text: 'Submit form'},
7272
{type: actionType.WEB_SWITCH_TO, text: 'Switch to'},
7373
{type: actionType.WEB_SWITCH_TO_FRAME, text: 'Switch to frame'},
74+
{type: actionType.WEB_UPLOAD_FILE, text: 'Upload file'},
7475
{type: actionType.WAIT_FOR_NODE_ATTRIBUTE, text: 'Wait for an attribute'},
7576
{type: actionType.WAIT_FOR_NODE, text: 'Wait for an element'},
7677
{type: actionType.WAIT_FOR_TEXT, text: 'Wait for text'},
77-
{type: actionType.WAIT_FOR_TITLE, text: 'Wait for page title'},
78+
{type: actionType.WAIT_FOR_TITLE, text: 'Wait for page title'}
7879
],
7980
rest: [
8081
{type: actionType.REST_CHECK_ATTRIBUTE_EXISTS, text: 'Check attribute'},

frontend/src/main/javascript/src/js/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export const actionType = {
9191
WEB_SUBMIT: 'web_submit',
9292
WEB_SWITCH_TO: 'web_switchTo',
9393
WEB_SWITCH_TO_FRAME: 'web_switchToFrame',
94+
WEB_UPLOAD_FILE: 'web_uploadFile',
9495
WEB_PRESS_KEY: 'web_pressKey',
9596
WAIT_FOR_NODE: 'web_waitForNode',
9697
WAIT_FOR_NODE_ATTRIBUTE: 'web_waitForNodeAttribute',
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2018 TU Dortmund
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {actionType} from '../../../constants';
18+
import {Action} from '../action';
19+
20+
/**
21+
* Searches for an element with a specific selector in the HTML document.
22+
*/
23+
export class UploadFileAction extends Action {
24+
25+
/**
26+
* Constructor.
27+
*
28+
* @param {object} obj - The object to create the action from.
29+
*/
30+
constructor(obj) {
31+
super(actionType.WEB_UPLOAD_FILE, obj);
32+
33+
/**
34+
* The selector of the node to search.
35+
* @type {Object}
36+
*/
37+
this.node = obj.node || {selector: '', type: 'CSS'};
38+
39+
/**
40+
* The name of the file to upload.
41+
* @type {string}
42+
*/
43+
this.fileName = obj.fileName || '';
44+
}
45+
46+
/**
47+
* A string representation of the action.
48+
*
49+
* @returns {string}
50+
*/
51+
toString() {
52+
return `Select file "${this.fileName}" to upload into element "${this.node.selector}"`;
53+
}
54+
}

frontend/src/main/javascript/src/js/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import {sendKeysActionFormComponent} from './components/forms/actions/web/send-k
7171
import {submitActionFormComponent} from './components/forms/actions/web/submit-action-form/submit-action-form.component';
7272
import {switchToFrameActionFormComponent} from './components/forms/actions/web/switch-to-frame/switch-to-frame-action-form.component';
7373
import {switchToActionFormComponent} from './components/forms/actions/web/switch-to/switch-to-action-form.component';
74+
import {uploadFileActionFormComponent} from './components/forms/actions/web/upload-file-action-form/upload-file-action-form.component';
7475
import {waitForNodeActionFormComponent} from './components/forms/actions/web/wait-for-node-action-form/wait-for-node-action-form.component';
7576
import {waitForNodeAttributeActionFormComponent} from './components/forms/actions/web/wait-for-node-attribute-action-form/wait-for-node-attribute-action-form.component';
7677
import {waitForTextActionFormComponent} from './components/forms/actions/web/wait-for-text-action-form/wait-for-text-action-form.component';
@@ -453,6 +454,7 @@ angular
453454
.component('waitForTextActionForm', waitForTextActionFormComponent)
454455
.component('pressKeyActionForm', pressKeyActionFormComponent)
455456
.component('waitForNodeAttributeActionForm', waitForNodeAttributeActionFormComponent)
457+
.component('uploadFileActionForm', uploadFileActionFormComponent)
456458

457459
// rest action forms
458460
.component('requestActionForm', requestActionFormComponent)

frontend/src/main/javascript/src/js/services/action.service.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {FillWebAction} from '../entities/actions/web/send-keys-action';
5858
import {SubmitWebAction} from '../entities/actions/web/submit-action';
5959
import {SwitchToAction} from '../entities/actions/web/switch-to-action';
6060
import {SwitchToFrameAction} from '../entities/actions/web/switch-to-frame';
61+
import {UploadFileAction} from '../entities/actions/web/upload-file-action';
6162
import {WaitForNodeAction} from '../entities/actions/web/wait-for-node-action';
6263
import {WaitForTextAction} from '../entities/actions/web/wait-for-text-action';
6364
import {WaitForTitleAction} from '../entities/actions/web/wait-for-title-action';
@@ -122,6 +123,8 @@ export class ActionService {
122123
return new SwitchToAction(data);
123124
case actionType.WEB_SWITCH_TO_FRAME:
124125
return new SwitchToFrameAction(data);
126+
case actionType.WEB_UPLOAD_FILE:
127+
return new UploadFileAction(data);
125128

126129
// rest actions
127130
case actionType.REST_CALL:

0 commit comments

Comments
 (0)