source: trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDgenerateSeleniumReplay.java @ 2146

Last change on this file since 2146 was 2146, checked in by pharms, 7 years ago
  • refactored GUI model so that hierarchical event target structures can also be used and created by plugins not being strictly for GUIs
File size: 15.0 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.html.commands;
16
17import java.io.BufferedWriter;
18import java.io.File;
19import java.io.FileWriter;
20import java.io.IOException;
21import java.net.HttpURLConnection;
22import java.net.MalformedURLException;
23import java.net.URL;
24import java.util.HashMap;
25import java.util.LinkedList;
26import java.util.List;
27import java.util.Map;
28import de.ugoe.cs.autoquest.CommandHelpers;
29import de.ugoe.cs.autoquest.SequenceInstanceOf;
30import de.ugoe.cs.autoquest.eventcore.Event;
31import de.ugoe.cs.autoquest.eventcore.IEventTarget;
32import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
33import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction.Button;
34import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
35import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
36import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
37import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument;
38import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElement;
39import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElement;
40import de.ugoe.cs.util.console.Command;
41import de.ugoe.cs.util.console.Console;
42import de.ugoe.cs.util.console.GlobalDataContainer;
43
44/**
45 * create XHTML files for Selenium from sequences
46 *
47 * @author Xiaowei Wang
48 * @version 1.0
49 */
50public class CMDgenerateSeleniumReplay implements Command {
51    /*
52     * (non-Javadoc)
53     *
54     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
55     */
56    @SuppressWarnings("unchecked")
57    @Override
58    public void run(List<Object> parameters) {
59        String sequencesName = "sequences";
60        String folder = null;
61        String baseURL = null; // default
62        try {
63            if (parameters.size() == 2) {
64                folder = (String) parameters.get(0);
65                baseURL = (String) parameters.get(1);
66            }
67            else if (parameters.size() == 3) {
68                sequencesName = (String) parameters.get(0);
69                folder = (String) parameters.get(1);
70                baseURL = (String) parameters.get(2);
71            }
72            else {
73                throw new IllegalArgumentException("The command needs two or three parameters.");
74            }
75
76            if (!(new File(folder).isDirectory())) {
77                throw new IllegalArgumentException("HTML files folder path: " + folder +
78                    " is invalid.");
79            }
80            // validate the basic URL
81            // HttpURLConnection.setFollowRedirects(false);
82            HttpURLConnection httpURLConnection =
83                (HttpURLConnection) new URL(baseURL).openConnection();
84            httpURLConnection.setRequestMethod("HEAD");
85            if (!(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK)) {
86                throw new IllegalArgumentException("Website basic URL: " + baseURL + " is invalid.");
87            }
88        }
89        catch (SecurityException se) {
90            throw new IllegalArgumentException("Parameters are invalid");
91        }
92        catch (MalformedURLException mue) {
93            throw new IllegalArgumentException("Website basic URL: " + baseURL + " is invalid.");
94        }
95        catch (IOException e) {
96            throw new IllegalArgumentException(e);
97        }
98
99        try {
100            List<List<Event>> sequences = null;
101            Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
102            if (dataObject == null) {
103                CommandHelpers.objectNotFoundMessage(sequencesName);
104                return;
105            }
106            if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
107                CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
108                return;
109            }
110            sequences = (List<List<Event>>) dataObject;
111
112            String command = null;
113            String target = null;
114            String value = null;
115            for (int seqsIndex = 0; seqsIndex < sequences.size(); seqsIndex++) {
116                LinkedList<Map<String, String>> attributeList =
117                    new LinkedList<Map<String, String>>();
118                List<Event> sequence = sequences.get(seqsIndex);
119                Event firstEvent = sequence.get(0);
120                HTMLGUIElement htmlGUIElement = (HTMLGUIElement) firstEvent.getTarget();
121                while (!(htmlGUIElement instanceof HTMLDocument)) { // the first target must be a
122                                                                    // page
123                    htmlGUIElement = (HTMLGUIElement) (htmlGUIElement.getParent());
124                }
125                command = "open";
126                target = ((HTMLDocument) htmlGUIElement).getPath();
127                value = "";
128                addToAttrList(attributeList, command, target, value);
129
130                for (int seqIndex = 1; seqIndex < sequence.size(); seqIndex++) {
131                    Event event = sequence.get(seqIndex);
132                    target = getXpath(event.getTarget());
133                    // Login button is global, must await the last page to load
134                    // otherwise, will lead to wrong page after logging in
135                    if (target.equals("//ul[@id='navlist2']/li[2]/a") &&
136                        (attributeList.size() > 1) &&
137                        !attributeList.getLast().get("command").endsWith("AndWait"))
138                    {
139                        attributeList.getLast().put("command",
140                                                    attributeList.getLast().get("command") +
141                                                        "AndWait");
142                    }
143
144                    if (event.getType() instanceof MouseClick &&
145                        (((MouseClick) event.getType()).getButton() == Button.LEFT || ((MouseClick) event
146                            .getType()).getButton() == Button.MIDDLE))
147                    {
148                        command = "click";
149                        String precedingCommand = "waitForElementPresent";
150                        value = "";
151                        addToAttrList(attributeList, precedingCommand, target, value);
152                    }
153                    else if (event.getType() instanceof TextInput) {
154                        command = "type";
155                        value = ((TextInput) event.getType()).getEnteredText();
156                        // if the session contains login process,
157                        // the session must log out at beginning
158                        if (target
159                            .equals("//form[@id='user-login']/div/div/input[@id='edit-name']") &&
160                            !attributeList.get(0).get("target").equals("/user/logout"))
161                        {
162                            addToAttrList(attributeList, "open", "/user/logout", "", 0);
163                        }
164                    }
165                    else if (event.getType() instanceof KeyPressed) {
166                        command = "sendKeys";
167                        VirtualKey pressedKey = ((KeyPressed) event.getType()).getKey();
168                        switch (pressedKey)
169                        {
170                            case ENTER:
171                                value = "${KEY_ENTER}";
172                                break;
173                            case TAB:
174                                value = "${KEY_TAB}";
175                                break;
176                            default:
177                                break;
178                        }
179                    }
180                    else {// Scroll or KeyboardFocusChange or else
181                        continue;
182                    }
183                    addToAttrList(attributeList, command, target, value);
184                }
185
186                if (attributeList.size() > 0) {
187                    createHTML(folder, "session-" + seqsIndex + ".html", baseURL, attributeList,
188                               "UTF-8");
189                }
190                else { // the sequence has no action
191                    continue;
192                }
193            }
194        }
195        catch (Exception e) {
196            Console.printerrln("Error on running the command.");
197            e.printStackTrace();
198        }
199    }
200
201    /**
202     * create event files in HTML with Selenium IDE template
203     *
204     * @param HTMLFolder
205     * @param fileName
206     * @param attributeList
207     */
208    private void createHTML(String HTMLFolder,
209                            String fileName,
210                            String baseURL,
211                            List<Map<String, String>> attributeList,
212                            String encoding)
213    {
214
215        String htmlStr =
216            "<?xml version=\"1.0\" encoding=\"${encoding}\"?>\n"
217                + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
218                + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
219                + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
220                + "<head profile=\"http://selenium-ide.openqa.org/profiles/test-case\">\n"
221                + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=${encoding}\" />\n"
222                + "<link rel=\"selenium.base\" href=\"${baseURL}\" />\n"
223                + "<title>${name}</title>\n" + "</head>\n" + "<body>\n"
224                + "<table cellpadding=\"1\" cellspacing=\"1\" border=\"1\">\n" + "<thead>\n"
225                + "<tr><td rowspan=\"1\" colspan=\"3\">${name}</td></tr>\n" + "</thead><tbody>\n";
226        htmlStr = htmlStr.replace("${encoding}", encoding);
227        htmlStr = htmlStr.replace("${baseURL}", baseURL);
228        htmlStr = htmlStr.replace("${name}", fileName.replace(".html", ""));
229        for (Map<String, String> commandMap : attributeList) {
230            htmlStr =
231                htmlStr + "<tr>\n" + "\t<td>" + commandMap.get("command") + "</td>\n" + "\t<td>" +
232                    commandMap.get("target") + "</td>\n" + "\t<td>" + commandMap.get("value") +
233                    "</td>\n" + "</tr>\n";
234        }
235
236        String fileEnding = "</tbody></table>\n</body>\n</html>";
237        htmlStr = htmlStr + fileEnding;
238        try {
239            File newHtmlFile =
240                new File(HTMLFolder + System.getProperty("file.separator") + fileName);
241            BufferedWriter bw = new BufferedWriter(new FileWriter(newHtmlFile));
242            bw.write(htmlStr);
243            bw.flush();
244            bw.close();
245        }
246        catch (Exception e) {
247            Console.printerrln("Error on creating xhtml files.");
248            e.printStackTrace();
249        }
250    }
251
252    /**
253     * add an event to the event list
254     *
255     * @param attributeList
256     * @param command
257     * @param target
258     * @param value
259     *
260     */
261    private void addToAttrList(LinkedList<Map<String, String>> attributeList,
262                               String command,
263                               String target,
264                               String value)
265    {
266        Map<String, String> ctvMap = new HashMap<String, String>();
267        ctvMap.put("command", command);
268        ctvMap.put("target", target);
269        ctvMap.put("value", value);
270        attributeList.add(ctvMap);
271    }
272
273    private void addToAttrList(LinkedList<Map<String, String>> attributeList,
274                               String command,
275                               String target,
276                               String value,
277                               int position)
278    {
279        Map<String, String> ctvMap = new HashMap<String, String>();
280        ctvMap.put("command", command);
281        ctvMap.put("target", target);
282        ctvMap.put("value", value);
283        attributeList.add(position, ctvMap);
284    }
285
286    /**
287     *
288     * @param iEventTarget
289     * @return the relative xpath
290     */
291    private String getXpath(IEventTarget iEventTarget) {
292        HTMLGUIElement htmlGUIElement = (HTMLGUIElement) iEventTarget;
293        List<String> xpathElements = new LinkedList<String>();
294        String htmlId = null;
295        int htmlIndex = 0;
296
297        // obtain the relative xpath of the element
298        while (!(htmlGUIElement instanceof HTMLDocument)) {
299            HTMLPageElement htmlPageElement = (HTMLPageElement) htmlGUIElement;
300            StringBuffer xpathElement = new StringBuffer();
301            // the real tag is "input" without _*
302            xpathElement.append(htmlPageElement.getTagName().replace("input_text", "input")
303                .replace("input_password", "input").replace("input_submit", "input"));
304            // better not use absolute xpath,
305            // Selenium IDE specifies only the
306            // 1st tag's id, if it's not the only tag
307            if ((htmlId = htmlPageElement.getHtmlId()) != null) {
308                xpathElement.append("[@id='");
309                xpathElement.append(htmlId);
310                xpathElement.append("']");
311                xpathElements.add(0, xpathElement.toString());
312                // the xpath must have more then one tag with id
313                // but the Selenium IDE uses only the 1st one
314                // which is enough for finding out the element
315                if (xpathElements.size() > 1) {
316                    break;
317                }
318            }
319            else if ((htmlIndex = htmlPageElement.getIndex()) != 0) {
320                xpathElement.append("[");
321                // find element by xpath, index starts from 1, not 0
322                xpathElement.append(htmlIndex + 1);
323                xpathElement.append("]");
324                xpathElements.add(0, xpathElement.toString());
325            }
326            else {
327                // if the index is 0, only add tag
328                xpathElements.add(0, xpathElement.toString());
329            }
330            htmlGUIElement = (HTMLGUIElement) (htmlGUIElement.getParent());
331        }
332
333        StringBuffer xPathStatement = new StringBuffer();
334        xPathStatement.append("/");
335        for (String xpathElem : xpathElements) {
336            xPathStatement.append("/");
337            xPathStatement.append(xpathElem);
338        }
339
340        return xPathStatement.toString();
341    }
342
343    /*
344     * (non-Javadoc)
345     *
346     * @see de.ugoe.cs.util.console.Command#help()
347     */
348    @Override
349    public String help() {
350
351        return "generateSeleniumReplay [sequenceName] <HTMLFilesFolder> <websiteBaseURL>";
352    }
353
354}
Note: See TracBrowser for help on using the repository browser.