View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
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    * https://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   */
15  package org.htmlunit.svg;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.BrowserRunner;
19  import org.htmlunit.junit.annotation.Alerts;
20  import org.junit.Assert;
21  import org.junit.Test;
22  import org.junit.runner.RunWith;
23  import org.openqa.selenium.WebDriver;
24  
25  /**
26   * Tests for {@link org.htmlunit.javascript.host.svg.SVGMatrix}.
27   *
28   * @author Marc Guillemot
29   * @author Frank Danek
30   * @author Ronald Brill
31   */
32  @RunWith(BrowserRunner.class)
33  public class SvgMatrixTest extends WebDriverTestCase {
34  
35      /**
36       * @throws Exception if the test fails
37       */
38      @Test
39      @Alerts("function SVGMatrix() { [native code] }")
40      public void simpleScriptable() throws Exception {
41          final String html = DOCTYPE_HTML
42              + "<html><body>\n"
43              + "<script>\n"
44              + LOG_TITLE_FUNCTION
45              + "  log(window.SVGMatrix);\n"
46              + "</script>\n"
47              + "</body></html>";
48  
49          loadPageVerifyTitle2(html);
50      }
51  
52      /**
53       * @throws Exception if the test fails
54       */
55      @Test
56      @Alerts({"1, 0, 0, 1, 0, 0", "2, 3, 4, 5, 6, 7"})
57      public void fields() throws Exception {
58          final String html = DOCTYPE_HTML
59              + "<html><body>\n"
60              + "  <svg xmlns='http://www.w3.org/2000/svg' id='myId' version='1.1'>\n"
61              + "  </svg>\n"
62              + "<script>\n"
63              + LOG_TITLE_FUNCTION
64              + "function alertFields(m) {\n"
65              + "  var fields = ['a', 'b', 'c', 'd', 'e', 'f'];\n"
66              + "  for (var i = 0; i < fields.length; i++) {\n"
67              + "    fields[i] = m[fields[i]];\n"
68              + "  }\n"
69              + "  log(fields.join(', '));\n"
70              + "}\n"
71              + "var svg =  document.getElementById('myId');\n"
72              + "try {\n"
73              + "  var m = svg.createSVGMatrix();\n"
74              + "  alertFields(m);\n"
75              + "  m.a = 2;\n"
76              + "  m.b = 3;\n"
77              + "  m.c = 4;\n"
78              + "  m.d = 5;\n"
79              + "  m.e = 6;\n"
80              + "  m.f = 7;\n"
81              + "  alertFields(m);\n"
82              + "} catch(e) { logEx(e); }\n"
83              + "</script>\n"
84              + "</body></html>";
85  
86          loadPageVerifyTitle2(html);
87      }
88  
89      /**
90       * @throws Exception if the test fails
91       */
92      @Test
93      @Alerts({"function", "function", "function", "function", "function", "function", "function", "function",
94               "function", "function", "function"})
95      public void methods() throws Exception {
96          final String html = DOCTYPE_HTML
97              + "<html><body>\n"
98              + "  <svg xmlns='http://www.w3.org/2000/svg' id='myId' version='1.1'>\n"
99              + "  </svg>\n"
100             + "<script>\n"
101             + LOG_TITLE_FUNCTION
102             + "  var svg =  document.getElementById('myId');\n"
103             + "try {\n"
104             + "  var m = svg.createSVGMatrix();\n"
105             + "  log(typeof m.flipX);\n"
106             + "  log(typeof m.flipY);\n"
107             + "  log(typeof m.inverse);\n"
108             + "  log(typeof m.multiply);\n"
109             + "  log(typeof m.rotate);\n"
110             + "  log(typeof m.rotateFromVector);\n"
111             + "  log(typeof m.scale);\n"
112             + "  log(typeof m.scaleNonUniform);\n"
113             + "  log(typeof m.skewX);\n"
114             + "  log(typeof m.skewY);\n"
115             + "  log(typeof m.translate);\n"
116             + "} catch(e) { logEx(e); }\n"
117             + "</script>\n"
118             + "</body></html>";
119 
120         loadPageVerifyTitle2(html);
121     }
122 
123     /**
124      * @throws Exception if the test fails
125      */
126     @Test
127     @Alerts({"false", "-1", "-2", "3", "4", "5", "6"})
128     public void flipX() throws Exception {
129         transformTest("flipX()");
130     }
131 
132     /**
133      * @throws Exception if the test fails
134      */
135     @Test
136     @Alerts({"false", "1", "2", "-3", "-4", "5", "6"})
137     public void flipY() throws Exception {
138         transformTest("flipY()");
139     }
140 
141     /**
142      * @throws Exception if the test fails
143      */
144     @Test
145     @Alerts({"false", "-2", "1", "1.5", "-0.5", "1", "-2"})
146     public void inverse() throws Exception {
147         transformTest("inverse()");
148     }
149 
150     /**
151      * @throws Exception if the test fails
152      */
153     @Test
154     @Alerts("InvalidStateError/DOMException")
155     public void inverseNotPossible() throws Exception {
156         final String html = DOCTYPE_HTML
157                 + "<html><body>\n"
158                 + "  <svg xmlns='http://www.w3.org/2000/svg' id='myId' version='1.1'>\n"
159                 + "  </svg>\n"
160                 + "<script>\n"
161                 + LOG_TITLE_FUNCTION
162                 + "function alertFields(m) {\n"
163                 + "  var fields = ['a', 'b', 'c', 'd', 'e', 'f'];\n"
164                 + "  for (var i = 0; i < fields.length; i++) {\n"
165                 + "    fields[i] = m[fields[i]];\n"
166                 + "  }\n"
167                 + "  log(fields.join(', '));\n"
168                 + "}\n"
169                 + "var svg =  document.getElementById('myId');\n"
170                 + "try {\n"
171                 + "  var m = svg.createSVGMatrix();\n"
172                 + "  m.a = 1;\n"
173                 + "  m.b = 1;\n"
174                 + "  m.c = 1;\n"
175                 + "  m.d = 1;\n"
176                 + "  m.e = 5;\n"
177                 + "  m.f = 6;\n"
178                 + "  m = m.inverse();\n"
179                 + "  alertFields(m);\n"
180                 + "} catch(e) { logEx(e); }\n"
181                 + "</script>\n"
182                 + "</body></html>";
183 
184         loadPageVerifyTitle2(html);
185     }
186 
187     /**
188      * @throws Exception if the test fails
189      */
190     @Test
191     @Alerts({"false", "25", "38", "17", "26", "14", "20"})
192     public void multiply() throws Exception {
193         transformTest("multiply(n)");
194     }
195 
196     /**
197      * @throws Exception if the test fails
198      */
199     @Test
200     @Alerts(DEFAULT = {"false", "1.2322946786880493", "2.307671070098877",
201                        "2.912292957305908", "3.8307511806488037", "5", "6"},
202             CHROME = {"false", "1.2322946209166628", "2.307671050377636",
203                       "2.912292905471539", "3.8307511434768218", "5", "6"},
204             EDGE = {"false", "1.2322946209166628", "2.307671050377636",
205                     "2.912292905471539", "3.8307511434768218", "5", "6"})
206     public void rotate() throws Exception {
207         transformTest("rotate(4.5)");
208     }
209 
210     /**
211      * @throws Exception if the test fails
212      */
213     @Test
214     @Alerts(DEFAULT = {"false", "3.147735595703125", "4.346245765686035",
215                        "-0.3029201924800873", "-1.0536353588104248", "5", "6"},
216             CHROME = {"false", "3.1477355949224934", "4.346245800520598",
217                       "-0.302920161854466", "-1.053635345580751", "5", "6"},
218             EDGE = {"false", "3.1477355949224934", "4.346245800520598",
219                     "-0.302920161854466", "-1.053635345580751", "5", "6"})
220     public void rotateFromVector() throws Exception {
221         transformTest("rotateFromVector(17, 74)");
222     }
223 
224     /**
225      * @throws Exception if the test fails
226      */
227     @Test
228     @Alerts("InvalidAccessError/DOMException")
229     public void rotateFromVectorZeroX() throws Exception {
230         transformTest("rotateFromVector(0, 74)");
231     }
232 
233     /**
234      * @throws Exception if the test fails
235      */
236     @Test
237     @Alerts("InvalidAccessError/DOMException")
238     public void rotateFromVectorZeroY() throws Exception {
239         transformTest("rotateFromVector(17, 0)");
240     }
241 
242     /**
243      * @throws Exception if the test fails
244      */
245     @Test
246     @Alerts("InvalidAccessError/DOMException")
247     public void rotateFromVectorZeroXY() throws Exception {
248         transformTest("rotateFromVector(0, 0)");
249     }
250 
251     /**
252      * @throws Exception if the test fails
253      */
254     @Test
255     @Alerts({"false", "3", "6", "9", "12", "5", "6"})
256     public void scale() throws Exception {
257         transformTest("scale(3)");
258     }
259 
260     /**
261      * @throws Exception if the test fails
262      */
263     @Test
264     @Alerts({"false", "7", "14", "21", "28", "5", "6"})
265     public void scaleNonUniform() throws Exception {
266         transformTest("scale(7, 22)");
267     }
268 
269     /**
270      * @throws Exception if the test fails
271      */
272     @Test
273     @Alerts(DEFAULT = {"false", "1", "2", "3.0699267387390137", "4.139853477478027", "5", "6"},
274             CHROME = {"false", "1", "2", "3.0699268119435104", "4.139853623887021", "5", "6"},
275             EDGE = {"false", "1", "2", "3.0699268119435104", "4.139853623887021", "5", "6"})
276     public void skewX() throws Exception {
277         transformTest("skewX(4)");
278     }
279 
280     /**
281      * @throws Exception if the test fails
282      */
283     @Test
284     @Alerts(DEFAULT = {"false", "1.6926045417785645", "2.9234728813171387", "3", "4", "5", "6"},
285             CHROME = {"false", "1.6926045733766895", "2.9234727645022525", "3", "4", "5", "6"},
286             EDGE = {"false", "1.6926045733766895", "2.9234727645022525", "3", "4", "5", "6"})
287     public void skewY() throws Exception {
288         transformTest("skewY(13)");
289     }
290 
291     /**
292      * @throws Exception if the test fails
293      */
294     @Test
295     @Alerts({"false", "1", "2", "3", "4", "69", "100"})
296     public void translate() throws Exception {
297         transformTest("translate(13 , 17)");
298     }
299 
300     private void transformTest(final String transforamtion) throws Exception {
301         final String html = DOCTYPE_HTML
302             + "<html><body>\n"
303             + "  <svg xmlns='http://www.w3.org/2000/svg' id='myId' version='1.1'>\n"
304             + "  </svg>\n"
305             + "<script>\n"
306             + LOG_TITLE_FUNCTION
307             + "function alertFields(m) {\n"
308             + "  var fields = ['a', 'b', 'c', 'd', 'e', 'f'];\n"
309             + "  for (var i = 0; i < fields.length; i++) {\n"
310             + "    log(m[fields[i]]);\n"
311             + "  }\n"
312             + "}\n"
313             + "var svg =  document.getElementById('myId');\n"
314             + "try {\n"
315             + "  var m = svg.createSVGMatrix();\n"
316             + "  m.a = 1;\n"
317             + "  m.b = 2;\n"
318             + "  m.c = 3;\n"
319             + "  m.d = 4;\n"
320             + "  m.e = 5;\n"
321             + "  m.f = 6;\n"
322 
323             + "  var n = svg.createSVGMatrix();\n"
324             + "  n.a = 7;\n"
325             + "  n.b = 6;\n"
326             + "  n.c = 5;\n"
327             + "  n.d = 4;\n"
328             + "  n.e = 3;\n"
329             + "  n.f = 2;\n"
330 
331             + "  r = m." + transforamtion + ";\n"
332             + "  log(m === r);\n"
333             + "  alertFields(r);\n"
334             + "} catch(e) { logEx(e); }\n"
335             + "</script>\n"
336             + "</body></html>";
337 
338         final String[] expectedAlerts = getExpectedAlerts();
339 
340         final WebDriver driver = loadPage2(html, URL_FIRST);
341         final String[] actualAlerts = driver.getTitle().split("§");
342 
343         assertEquals(expectedAlerts.length, actualAlerts.length);
344         if (useRealBrowser()) {
345             for (int i = 0; i < expectedAlerts.length; i++) {
346                 assertEquals("Expected: " + String.join(",", expectedAlerts)
347                                 + " Actual: " + String.join(",", actualAlerts),
348                         expectedAlerts[i], actualAlerts[i]);
349             }
350         }
351         else {
352             for (int i = 0; i < expectedAlerts.length; i++) {
353                 try {
354                     Assert.assertEquals(
355                             Double.parseDouble(expectedAlerts[i]),
356                             Double.parseDouble(actualAlerts[i]), 0.000001);
357                 }
358                 catch (final NumberFormatException e) {
359                     assertEquals(expectedAlerts[i], actualAlerts[i]);
360                 }
361             }
362         }
363     }
364 }