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