Procházet zdrojové kódy

test: transition tests

Evan You před 3 roky
rodič
revize
04b584ffc1

+ 1 - 0
.github/workflows/ci.yml

@@ -63,6 +63,7 @@ jobs:
 
       - name: Run e2e tests
         run: pnpm run test:e2e
+        run: pnpm run test:transition
 
   lint-and-test-dts:
     runs-on: ubuntu-latest

+ 8 - 0
package.json

@@ -26,6 +26,7 @@
     "test:unit": "vitest run test/unit",
     "test:ssr": "npm run build:ssr && vitest run test/ssr",
     "test:e2e": "npm run build -- web-full-prod,web-server-renderer-basic && vitest run test/e2e",
+    "test:transition": "karma start test/transition/karma.conf.js",
     "test:types": "tsc -p ./types/tsconfig.json",
     "lint": "eslint src scripts test",
     "ts-check": "tsc -p tsconfig.json --noEmit",
@@ -74,12 +75,19 @@
     "cross-spawn": "^7.0.3",
     "cz-conventional-changelog": "^3.3.0",
     "de-indent": "^1.0.2",
+    "esbuild": "^0.14.39",
     "escodegen": "^2.0.0",
     "eslint": "^8.14.0",
     "file-loader": "^3.0.1",
     "hash-sum": "^2.0.0",
     "he": "^1.2.0",
+    "jasmine-core": "^4.1.1",
     "jsdom": "^19.0.0",
+    "karma": "^6.3.20",
+    "karma-chrome-launcher": "^3.1.1",
+    "karma-cli": "^2.0.0",
+    "karma-esbuild": "^2.2.4",
+    "karma-jasmine": "^5.0.1",
     "lint-staged": "^12.4.1",
     "lodash": "^4.17.21",
     "lodash.template": "^4.4.0",

+ 527 - 5
pnpm-lock.yaml

@@ -17,12 +17,19 @@ specifiers:
   cross-spawn: ^7.0.3
   cz-conventional-changelog: ^3.3.0
   de-indent: ^1.0.2
+  esbuild: ^0.14.39
   escodegen: ^2.0.0
   eslint: ^8.14.0
   file-loader: ^3.0.1
   hash-sum: ^2.0.0
   he: ^1.2.0
+  jasmine-core: ^4.1.1
   jsdom: ^19.0.0
+  karma: ^6.3.20
+  karma-chrome-launcher: ^3.1.1
+  karma-cli: ^2.0.0
+  karma-esbuild: ^2.2.4
+  karma-jasmine: ^5.0.1
   lint-staged: ^12.4.1
   lodash: ^4.17.21
   lodash.template: ^4.4.0
@@ -63,12 +70,19 @@ devDependencies:
   cross-spawn: 7.0.3
   cz-conventional-changelog: 3.3.0
   de-indent: 1.0.2
+  esbuild: 0.14.39
   escodegen: 2.0.0
   eslint: 8.15.0
   file-loader: 3.0.1_webpack@4.46.0
   hash-sum: 2.0.0
   he: 1.2.0
+  jasmine-core: 4.1.1
   jsdom: 19.0.0
+  karma: 6.3.20
+  karma-chrome-launcher: 3.1.1
+  karma-cli: 2.0.0
+  karma-esbuild: 2.2.4_esbuild@0.14.39
+  karma-jasmine: 5.0.1_karma@6.3.20
   lint-staged: 12.4.1
   lodash: 4.17.21
   lodash.template: 4.5.0
@@ -115,6 +129,11 @@ packages:
       js-tokens: 4.0.0
     dev: true
 
+  /@colors/colors/1.5.0:
+    resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
+    engines: {node: '>=0.1.90'}
+    dev: true
+
   /@commitlint/config-validator/17.0.0:
     resolution: {integrity: sha512-78IQjoZWR4kDHp/U5y17euEWzswJpPkA9TDL5F6oZZZaLIEreWzrDZD5PWtM8MsSRl/K2LDU/UrzYju2bKLMpA==}
     engines: {node: '>=v14'}
@@ -345,6 +364,18 @@ packages:
     resolution: {integrity: sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==}
     dev: true
 
+  /@types/component-emitter/1.2.11:
+    resolution: {integrity: sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==}
+    dev: true
+
+  /@types/cookie/0.4.1:
+    resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
+    dev: true
+
+  /@types/cors/2.8.12:
+    resolution: {integrity: sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==}
+    dev: true
+
   /@types/estree/0.0.39:
     resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
     dev: true
@@ -673,6 +704,14 @@ packages:
     resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
     dev: true
 
+  /accepts/1.3.8:
+    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-types: 2.1.35
+      negotiator: 0.6.3
+    dev: true
+
   /acorn-globals/6.0.0:
     resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
     dependencies:
@@ -830,7 +869,6 @@ packages:
       normalize-path: 3.0.0
       picomatch: 2.3.1
     dev: true
-    optional: true
 
   /aproba/1.2.0:
     resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
@@ -944,6 +982,11 @@ packages:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
     dev: true
 
+  /base64id/2.0.0:
+    resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
+    engines: {node: ^4.5.0 || >= 5.9}
+    dev: true
+
   /big.js/5.2.2:
     resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
     dev: true
@@ -958,7 +1001,6 @@ packages:
     resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
     engines: {node: '>=8'}
     dev: true
-    optional: true
 
   /bindings/1.5.0:
     resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
@@ -987,6 +1029,26 @@ packages:
     resolution: {integrity: sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==}
     dev: true
 
+  /body-parser/1.20.0:
+    resolution: {integrity: sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+    dependencies:
+      bytes: 3.1.2
+      content-type: 1.0.4
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      http-errors: 2.0.0
+      iconv-lite: 0.4.24
+      on-finished: 2.4.1
+      qs: 6.10.3
+      raw-body: 2.5.1
+      type-is: 1.6.18
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /brace-expansion/1.1.11:
     resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
     dependencies:
@@ -1118,6 +1180,11 @@ packages:
     resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
     dev: true
 
+  /bytes/3.1.2:
+    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /cacache/12.0.4:
     resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==}
     dependencies:
@@ -1158,6 +1225,13 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /call-bind/1.0.2:
+    resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
+    dependencies:
+      function-bind: 1.1.1
+      get-intrinsic: 1.1.1
+    dev: true
+
   /callsites/3.1.0:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
@@ -1252,7 +1326,6 @@ packages:
     optionalDependencies:
       fsevents: 2.3.2
     dev: true
-    optional: true
 
   /chownr/1.1.4:
     resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@@ -1433,6 +1506,18 @@ packages:
       typedarray: 0.0.6
     dev: true
 
+  /connect/3.7.0:
+    resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==}
+    engines: {node: '>= 0.10.0'}
+    dependencies:
+      debug: 2.6.9
+      finalhandler: 1.1.2
+      parseurl: 1.3.3
+      utils-merge: 1.0.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /console-browserify/1.2.0:
     resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==}
     dev: true
@@ -1441,6 +1526,11 @@ packages:
     resolution: {integrity: sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=}
     dev: true
 
+  /content-type/1.0.4:
+    resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
   /conventional-changelog-angular/5.0.13:
     resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==}
     engines: {node: '>=10'}
@@ -1591,6 +1681,11 @@ packages:
       through2: 4.0.2
     dev: true
 
+  /cookie/0.4.2:
+    resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
   /copy-concurrently/1.0.5:
     resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==}
     dependencies:
@@ -1611,6 +1706,14 @@ packages:
     resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
     dev: true
 
+  /cors/2.8.5:
+    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      object-assign: 4.1.1
+      vary: 1.1.2
+    dev: true
+
   /cosmiconfig-typescript-loader/2.0.0_3smuweqyuzdazdnyhhezld6mfa:
     resolution: {integrity: sha512-2NlGul/E3vTQEANqPziqkA01vfiuUU8vT0jZAuUIjEW8u3eCcnCQWLggapCjhbF76s7KQF0fM0kXSKmzaDaG1g==}
     engines: {node: '>=12', npm: '>=6'}
@@ -1728,6 +1831,10 @@ packages:
       cssom: 0.3.8
     dev: true
 
+  /custom-event/1.0.1:
+    resolution: {integrity: sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=}
+    dev: true
+
   /cyclist/1.0.1:
     resolution: {integrity: sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=}
     dev: true
@@ -1780,6 +1887,11 @@ packages:
       whatwg-url: 11.0.0
     dev: true
 
+  /date-format/4.0.10:
+    resolution: {integrity: sha512-RuMIHocrVjF84bUSTcd1uokIsLsOsk1Awb7TexNOI3f48ukCu39mjslWquDTA08VaDMF2umr3MB9ow5EyJTWyA==}
+    engines: {node: '>=4.0'}
+    dev: true
+
   /dateformat/3.0.3:
     resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
     dev: true
@@ -1893,6 +2005,11 @@ packages:
     engines: {node: '>=0.4.0'}
     dev: true
 
+  /depd/2.0.0:
+    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /des.js/1.0.1:
     resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==}
     dependencies:
@@ -1900,6 +2017,11 @@ packages:
       minimalistic-assert: 1.0.1
     dev: true
 
+  /destroy/1.2.0:
+    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+    dev: true
+
   /detect-file/1.0.0:
     resolution: {integrity: sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=}
     engines: {node: '>=0.10.0'}
@@ -1914,6 +2036,10 @@ packages:
     resolution: {integrity: sha512-FnVW2nDbjGNw1uD/JRC+9U5768W7e1TfUwqbDTcSsAu1jXFjITSX8w3rkW5FEpHRMPPGpvNSmO1pOpqByiWscA==}
     dev: true
 
+  /di/0.0.1:
+    resolution: {integrity: sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=}
+    dev: true
+
   /diff/4.0.2:
     resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
     engines: {node: '>=0.3.1'}
@@ -1941,6 +2067,15 @@ packages:
       esutils: 2.0.3
     dev: true
 
+  /dom-serialize/2.2.1:
+    resolution: {integrity: sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=}
+    dependencies:
+      custom-event: 1.0.1
+      ent: 2.2.0
+      extend: 3.0.2
+      void-elements: 2.0.1
+    dev: true
+
   /domain-browser/1.2.0:
     resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
     engines: {node: '>=0.4', npm: '>=1.2'}
@@ -1973,6 +2108,10 @@ packages:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
     dev: true
 
+  /ee-first/1.1.1:
+    resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
+    dev: true
+
   /elliptic/6.5.4:
     resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
     dependencies:
@@ -1998,12 +2137,42 @@ packages:
     engines: {node: '>= 4'}
     dev: true
 
+  /encodeurl/1.0.2:
+    resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /end-of-stream/1.4.4:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
     dependencies:
       once: 1.4.0
     dev: true
 
+  /engine.io-parser/5.0.4:
+    resolution: {integrity: sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==}
+    engines: {node: '>=10.0.0'}
+    dev: true
+
+  /engine.io/6.2.0:
+    resolution: {integrity: sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      '@types/cookie': 0.4.1
+      '@types/cors': 2.8.12
+      '@types/node': 17.0.34
+      accepts: 1.3.8
+      base64id: 2.0.0
+      cookie: 0.4.2
+      cors: 2.8.5
+      debug: 4.3.4
+      engine.io-parser: 5.0.4
+      ws: 8.2.3
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
   /enhanced-resolve/4.5.0:
     resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==}
     engines: {node: '>=6.9.0'}
@@ -2013,6 +2182,10 @@ packages:
       tapable: 1.1.3
     dev: true
 
+  /ent/2.2.0:
+    resolution: {integrity: sha1-6WQhkyWiHQX0RGai9obtbOX13R0=}
+    dev: true
+
   /errno/0.1.8:
     resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
     hasBin: true
@@ -2239,6 +2412,10 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /escape-html/1.0.3:
+    resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=}
+    dev: true
+
   /escape-string-regexp/1.0.5:
     resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
     engines: {node: '>=0.8.0'}
@@ -2402,6 +2579,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /eventemitter3/4.0.7:
+    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+    dev: true
+
   /events/3.3.0:
     resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
     engines: {node: '>=0.8.x'}
@@ -2479,6 +2660,10 @@ packages:
       is-extendable: 1.0.1
     dev: true
 
+  /extend/3.0.2:
+    resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+    dev: true
+
   /external-editor/3.1.0:
     resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
     engines: {node: '>=4'}
@@ -2604,6 +2789,21 @@ packages:
       to-regex-range: 5.0.1
     dev: true
 
+  /finalhandler/1.1.2:
+    resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      debug: 2.6.9
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      on-finished: 2.3.0
+      parseurl: 1.3.3
+      statuses: 1.5.0
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /find-cache-dir/2.1.0:
     resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
     engines: {node: '>=6'}
@@ -2692,6 +2892,16 @@ packages:
       readable-stream: 2.3.7
     dev: true
 
+  /follow-redirects/1.15.0:
+    resolution: {integrity: sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+    dev: true
+
   /for-in/1.0.2:
     resolution: {integrity: sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=}
     engines: {node: '>=0.10.0'}
@@ -2791,6 +3001,14 @@ packages:
     resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=}
     dev: true
 
+  /get-intrinsic/1.1.1:
+    resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
+    dependencies:
+      function-bind: 1.1.1
+      has: 1.0.3
+      has-symbols: 1.0.3
+    dev: true
+
   /get-pkg-repo/4.2.1:
     resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==}
     engines: {node: '>=6.9.0'}
@@ -2982,6 +3200,11 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /has-symbols/1.0.3:
+    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
   /has-value/0.3.1:
     resolution: {integrity: sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=}
     engines: {node: '>=0.10.0'}
@@ -3078,6 +3301,17 @@ packages:
       whatwg-encoding: 2.0.0
     dev: true
 
+  /http-errors/2.0.0:
+    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      depd: 2.0.0
+      inherits: 2.0.4
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      toidentifier: 1.0.1
+    dev: true
+
   /http-proxy-agent/5.0.0:
     resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
     engines: {node: '>= 6'}
@@ -3089,6 +3323,17 @@ packages:
       - supports-color
     dev: true
 
+  /http-proxy/1.18.1:
+    resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
+    engines: {node: '>=8.0.0'}
+    dependencies:
+      eventemitter3: 4.0.7
+      follow-redirects: 1.15.0
+      requires-port: 1.0.0
+    transitivePeerDependencies:
+      - debug
+    dev: true
+
   /https-browserify/1.0.0:
     resolution: {integrity: sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=}
     dev: true
@@ -3236,7 +3481,6 @@ packages:
     dependencies:
       binary-extensions: 2.2.0
     dev: true
-    optional: true
 
   /is-buffer/1.1.6:
     resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
@@ -3419,6 +3663,11 @@ packages:
     resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=}
     dev: true
 
+  /isbinaryfile/4.0.10:
+    resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
+    engines: {node: '>= 8.0.0'}
+    dev: true
+
   /isexe/2.0.0:
     resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
     dev: true
@@ -3435,6 +3684,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /jasmine-core/4.1.1:
+    resolution: {integrity: sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==}
+    dev: true
+
   /js-tokens/4.0.0:
     resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
     dev: true
@@ -3534,6 +3787,76 @@ packages:
     engines: {'0': node >= 0.2.0}
     dev: true
 
+  /karma-chrome-launcher/3.1.1:
+    resolution: {integrity: sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==}
+    dependencies:
+      which: 1.3.1
+    dev: true
+
+  /karma-cli/2.0.0:
+    resolution: {integrity: sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==}
+    engines: {node: '>= 6'}
+    hasBin: true
+    dependencies:
+      resolve: 1.22.0
+    dev: true
+
+  /karma-esbuild/2.2.4_esbuild@0.14.39:
+    resolution: {integrity: sha512-nt5+AJ0iFpgJXboBmXvwjM5LYTqTmFA9NuNGj9A442gCMJHwL4Eft4JUBVRc7ORSPoJAQ26Kgf5252Xt6GbkRw==}
+    peerDependencies:
+      esbuild: '>=0.8.45'
+    dependencies:
+      chokidar: 3.5.3
+      esbuild: 0.14.39
+      source-map: 0.6.1
+    dev: true
+
+  /karma-jasmine/5.0.1_karma@6.3.20:
+    resolution: {integrity: sha512-FkL1Kk+JAKmim8VWU8RXKZBpl0lLI7J8LijM0/q7oP7emfB6QMZV1Az+JgqGKSLpF0tYaav+KUVFQroZUxQTHA==}
+    engines: {node: '>=12'}
+    peerDependencies:
+      karma: ^6.0.0
+    dependencies:
+      jasmine-core: 4.1.1
+      karma: 6.3.20
+    dev: true
+
+  /karma/6.3.20:
+    resolution: {integrity: sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==}
+    engines: {node: '>= 10'}
+    hasBin: true
+    dependencies:
+      '@colors/colors': 1.5.0
+      body-parser: 1.20.0
+      braces: 3.0.2
+      chokidar: 3.5.3
+      connect: 3.7.0
+      di: 0.0.1
+      dom-serialize: 2.2.1
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      http-proxy: 1.18.1
+      isbinaryfile: 4.0.10
+      lodash: 4.17.21
+      log4js: 6.5.0
+      mime: 2.6.0
+      minimatch: 3.1.2
+      mkdirp: 0.5.6
+      qjobs: 1.2.0
+      range-parser: 1.2.1
+      rimraf: 3.0.2
+      socket.io: 4.5.1
+      source-map: 0.6.1
+      tmp: 0.2.1
+      ua-parser-js: 0.7.31
+      yargs: 16.2.0
+    transitivePeerDependencies:
+      - bufferutil
+      - debug
+      - supports-color
+      - utf-8-validate
+    dev: true
+
   /kind-of/3.2.2:
     resolution: {integrity: sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=}
     engines: {node: '>=0.10.0'}
@@ -3735,6 +4058,19 @@ packages:
       wrap-ansi: 6.2.0
     dev: true
 
+  /log4js/6.5.0:
+    resolution: {integrity: sha512-VT8b+k4nS7feV3p2nTdI5UqO2+7KRi4sfb6YKlnZlQU0d1DnvA18dvPhxKElgVTRTjS3GtChvvR67yMpp/xAfQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      date-format: 4.0.10
+      debug: 4.3.4
+      flatted: 3.2.5
+      rfdc: 1.3.0
+      streamroller: 3.1.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /longest/2.0.1:
     resolution: {integrity: sha1-eB4YMpaqlPbU2RbcM10NF676I/g=}
     engines: {node: '>=0.10.0'}
@@ -3832,6 +4168,11 @@ packages:
       safe-buffer: 5.2.1
     dev: true
 
+  /media-typer/0.3.0:
+    resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
+    engines: {node: '>= 0.6'}
+    dev: true
+
   /memory-fs/0.4.1:
     resolution: {integrity: sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=}
     dependencies:
@@ -3926,6 +4267,12 @@ packages:
       mime-db: 1.52.0
     dev: true
 
+  /mime/2.6.0:
+    resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
+    engines: {node: '>=4.0.0'}
+    hasBin: true
+    dev: true
+
   /mimic-fn/1.2.0:
     resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
     engines: {node: '>=4'}
@@ -4069,6 +4416,11 @@ packages:
     resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=}
     dev: true
 
+  /negotiator/0.6.3:
+    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
   /neo-async/2.6.2:
     resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
     dev: true
@@ -4200,6 +4552,20 @@ packages:
       isobject: 3.0.1
     dev: true
 
+  /on-finished/2.3.0:
+    resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      ee-first: 1.1.1
+    dev: true
+
+  /on-finished/2.4.1:
+    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      ee-first: 1.1.1
+    dev: true
+
   /once/1.4.0:
     resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
     dependencies:
@@ -4380,6 +4746,11 @@ packages:
     resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
     dev: true
 
+  /parseurl/1.3.3:
+    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /pascalcase/0.1.1:
     resolution: {integrity: sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=}
     engines: {node: '>=0.10.0'}
@@ -4657,6 +5028,18 @@ packages:
     engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
     dev: true
 
+  /qjobs/1.2.0:
+    resolution: {integrity: sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==}
+    engines: {node: '>=0.9'}
+    dev: true
+
+  /qs/6.10.3:
+    resolution: {integrity: sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==}
+    engines: {node: '>=0.6'}
+    dependencies:
+      side-channel: 1.0.4
+    dev: true
+
   /querystring-es3/0.2.1:
     resolution: {integrity: sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=}
     engines: {node: '>=0.4.x'}
@@ -4690,6 +5073,21 @@ packages:
       safe-buffer: 5.2.1
     dev: true
 
+  /range-parser/1.2.1:
+    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /raw-body/2.5.1:
+    resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      bytes: 3.1.2
+      http-errors: 2.0.0
+      iconv-lite: 0.4.24
+      unpipe: 1.0.0
+    dev: true
+
   /read-pkg-up/3.0.0:
     resolution: {integrity: sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=}
     engines: {node: '>=4'}
@@ -4765,7 +5163,6 @@ packages:
     dependencies:
       picomatch: 2.3.1
     dev: true
-    optional: true
 
   /rechoir/0.6.2:
     resolution: {integrity: sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=}
@@ -4815,6 +5212,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /requires-port/1.0.0:
+    resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=}
+    dev: true
+
   /resolve-dir/1.0.1:
     resolution: {integrity: sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=}
     engines: {node: '>=0.10.0'}
@@ -5039,6 +5440,10 @@ packages:
     resolution: {integrity: sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=}
     dev: true
 
+  /setprototypeof/1.2.0:
+    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+    dev: true
+
   /sha.js/2.4.11:
     resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
     hasBin: true
@@ -5081,6 +5486,14 @@ packages:
       rechoir: 0.6.2
     dev: true
 
+  /side-channel/1.0.4:
+    resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+    dependencies:
+      call-bind: 1.0.2
+      get-intrinsic: 1.1.1
+      object-inspect: 1.12.0
+    dev: true
+
   /signal-exit/3.0.7:
     resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
     dev: true
@@ -5148,6 +5561,37 @@ packages:
       - supports-color
     dev: true
 
+  /socket.io-adapter/2.4.0:
+    resolution: {integrity: sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==}
+    dev: true
+
+  /socket.io-parser/4.0.4:
+    resolution: {integrity: sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      '@types/component-emitter': 1.2.11
+      component-emitter: 1.3.0
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /socket.io/4.5.1:
+    resolution: {integrity: sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      accepts: 1.3.8
+      base64id: 2.0.0
+      debug: 4.3.4
+      engine.io: 6.2.0
+      socket.io-adapter: 2.4.0
+      socket.io-parser: 4.0.4
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
   /source-list-map/2.0.1:
     resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
     dev: true
@@ -5256,6 +5700,16 @@ packages:
       object-copy: 0.1.0
     dev: true
 
+  /statuses/1.5.0:
+    resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /statuses/2.0.1:
+    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /stream-browserify/2.0.2:
     resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
     dependencies:
@@ -5284,6 +5738,17 @@ packages:
     resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
     dev: true
 
+  /streamroller/3.1.0:
+    resolution: {integrity: sha512-9+fOFZX3qeo5lEdq9AnPxyRqdrh0MhlX5uqDN6+OLGuGK0PnyyKjMicwcUYIzjYRut5Pfbc0AV6WKdv6di3YGA==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      date-format: 4.0.10
+      debug: 4.3.4
+      fs-extra: 10.1.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /string-argv/0.3.1:
     resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
     engines: {node: '>=0.6.19'}
@@ -5540,6 +6005,13 @@ packages:
       os-tmpdir: 1.0.2
     dev: true
 
+  /tmp/0.2.1:
+    resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
+    engines: {node: '>=8.17.0'}
+    dependencies:
+      rimraf: 3.0.2
+    dev: true
+
   /to-arraybuffer/1.0.1:
     resolution: {integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=}
     dev: true
@@ -5580,6 +6052,11 @@ packages:
     resolution: {integrity: sha512-ViAkQ7ed89rmhFIGRsT36njN+97z8+s3XsJnB8E2IKOq+/SLD/6PtSvmTtiwUcVk39qPcjAc/OyeDys4LoJUVg==}
     dev: true
 
+  /toidentifier/1.0.1:
+    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+    engines: {node: '>=0.6'}
+    dev: true
+
   /tough-cookie/4.0.0:
     resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==}
     engines: {node: '>=6'}
@@ -5708,6 +6185,14 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /type-is/1.6.18:
+    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      media-typer: 0.3.0
+      mime-types: 2.1.35
+    dev: true
+
   /typedarray/0.0.6:
     resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
     dev: true
@@ -5718,6 +6203,10 @@ packages:
     hasBin: true
     dev: true
 
+  /ua-parser-js/0.7.31:
+    resolution: {integrity: sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==}
+    dev: true
+
   /uglify-js/3.15.5:
     resolution: {integrity: sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==}
     engines: {node: '>=0.8.0'}
@@ -5765,6 +6254,11 @@ packages:
     engines: {node: '>= 10.0.0'}
     dev: true
 
+  /unpipe/1.0.0:
+    resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /unset-value/1.0.0:
     resolution: {integrity: sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=}
     engines: {node: '>=0.10.0'}
@@ -5827,6 +6321,11 @@ packages:
       inherits: 2.0.3
     dev: true
 
+  /utils-merge/1.0.1:
+    resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=}
+    engines: {node: '>= 0.4.0'}
+    dev: true
+
   /v8-compile-cache-lib/3.0.1:
     resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
     dev: true
@@ -5842,6 +6341,11 @@ packages:
       spdx-expression-parse: 3.0.1
     dev: true
 
+  /vary/1.1.2:
+    resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
+    engines: {node: '>= 0.8'}
+    dev: true
+
   /vite/2.9.9:
     resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==}
     engines: {node: '>=12.2.0'}
@@ -5903,6 +6407,11 @@ packages:
     resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
     dev: true
 
+  /void-elements/2.0.1:
+    resolution: {integrity: sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
   /w3c-hr-time/1.0.2:
     resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
     dependencies:
@@ -6093,6 +6602,19 @@ packages:
     resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
     dev: true
 
+  /ws/8.2.3:
+    resolution: {integrity: sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: ^5.0.2
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+    dev: true
+
   /ws/8.6.0:
     resolution: {integrity: sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==}
     engines: {node: '>=10.0.0'}

+ 1 - 1
test/helpers/trigger-event.ts

@@ -1,4 +1,4 @@
-global.triggerEvent = function triggerEvent (target, event, process) {
+export function triggerEvent (target, event, process) {
   const e = document.createEvent('HTMLEvents')
   e.initEvent(event, true, true)
   if (event === 'click') {

+ 1 - 1
test/helpers/vdom.ts

@@ -1,5 +1,5 @@
 import VNode from 'core/vdom/vnode'
 
-global.createTextVNode = function (text) {
+export function createTextVNode(text) {
   return new VNode(undefined, undefined, undefined, text)
 }

+ 3 - 1
test/helpers/wait-for-update.ts

@@ -18,7 +18,7 @@ interface Job extends Function {
   fail?: (e: any) => void
 }
 
-global.waitForUpdate = (initialCb: Job) => {
+const waitForUpdate = (initialCb: Job) => {
   let end
   const queue: Job[] = initialCb ? [initialCb] : []
 
@@ -77,3 +77,5 @@ global.waitForUpdate = (initialCb: Job) => {
 function timeout(n) {
   return (next) => setTimeout(next, n)
 }
+
+export { waitForUpdate }

+ 10 - 0
test/test-env.d.ts

@@ -24,3 +24,13 @@ declare namespace jest {
     toHaveClass(cls: string): R
   }
 }
+
+declare const jasmine: {
+  createSpy: (id?: string) => {
+    (...args: any[]): any
+    calls: {
+      count(): number
+    }
+  }
+  addMatchers(matchers: any): void
+}

+ 144 - 0
test/transition/helpers.ts

@@ -0,0 +1,144 @@
+export { waitForUpdate } from '../helpers/wait-for-update'
+export { nextFrame } from 'web/runtime/transition-util'
+
+// toHaveBeenWarned() matcher
+function noop() {}
+
+if (typeof console === 'undefined') {
+  // @ts-ignore
+  window.console = {
+    warn: noop,
+    error: noop
+  }
+}
+
+// avoid info messages during test
+console.info = noop
+
+let asserted
+
+function createCompareFn(spy) {
+  const hasWarned = (msg) => {
+    let count = spy.calls.count()
+    let args
+    while (count--) {
+      args = spy.calls.argsFor(count)
+      if (args.some(containsMsg)) {
+        return true
+      }
+    }
+
+    function containsMsg(arg) {
+      return arg.toString().indexOf(msg) > -1
+    }
+  }
+
+  return {
+    compare: (msg) => {
+      asserted = asserted.concat(msg)
+      const warned = Array.isArray(msg) ? msg.some(hasWarned) : hasWarned(msg)
+      return {
+        pass: warned,
+        message: warned
+          ? 'Expected message "' + msg + '" not to have been warned'
+          : 'Expected message "' + msg + '" to have been warned'
+      }
+    }
+  }
+}
+
+// define custom matcher for warnings
+beforeEach(() => {
+  asserted = []
+  // @ts-ignore
+  spyOn(console, 'warn')
+  // @ts-ignore
+  spyOn(console, 'error')
+  jasmine.addMatchers({
+    toHaveBeenWarned: () => createCompareFn(console.error),
+    toHaveBeenTipped: () => createCompareFn(console.warn)
+  })
+})
+
+afterEach((done) => {
+  const warned = (msg) =>
+    asserted.some((assertedMsg) => msg.toString().indexOf(assertedMsg) > -1)
+  // @ts-ignore
+  let count = console.error.calls.count()
+  let args
+  while (count--) {
+    // @ts-ignore
+    args = console.error.calls.argsFor(count)
+    if (!warned(args[0])) {
+      // @ts-ignore
+      done.fail(`Unexpected console.error message: ${args[0]}`)
+      return
+    }
+  }
+  done()
+})
+
+// injectStyles helper
+function insertCSS(text) {
+  const cssEl = document.createElement('style')
+  cssEl.textContent = text.trim()
+  document.head.appendChild(cssEl)
+}
+
+const duration = process.env.CI ? 200 : 50
+const buffer = process.env.CI ? 20 : 5
+let injected = false
+
+export function injectStyles() {
+  if (injected) return { duration, buffer }
+  injected = true
+  insertCSS(`
+    .test {
+      -webkit-transition: opacity ${duration}ms ease;
+      transition: opacity ${duration}ms ease;
+    }
+    .group-move {
+      -webkit-transition: -webkit-transform ${duration}ms ease;
+      transition: transform ${duration}ms ease;
+    }
+    .v-appear, .v-enter, .v-leave-active,
+    .test-appear, .test-enter, .test-leave-active,
+    .hello, .bye.active,
+    .changed-enter {
+      opacity: 0;
+    }
+    .test-anim-enter-active {
+      animation: test-enter ${duration}ms;
+      -webkit-animation: test-enter ${duration}ms;
+    }
+    .test-anim-leave-active {
+      animation: test-leave ${duration}ms;
+      -webkit-animation: test-leave ${duration}ms;
+    }
+    .test-anim-long-enter-active {
+      animation: test-enter ${duration * 2}ms;
+      -webkit-animation: test-enter ${duration * 2}ms;
+    }
+    .test-anim-long-leave-active {
+      animation: test-leave ${duration * 2}ms;
+      -webkit-animation: test-leave ${duration * 2}ms;
+    }
+    @keyframes test-enter {
+      from { opacity: 0 }
+      to { opacity: 1 }
+    }
+    @-webkit-keyframes test-enter {
+      from { opacity: 0 }
+      to { opacity: 1 }
+    }
+    @keyframes test-leave {
+      from { opacity: 1 }
+      to { opacity: 0 }
+    }
+    @-webkit-keyframes test-leave {
+      from { opacity: 1 }
+      to { opacity: 0 }
+    }
+  `)
+  return { duration, buffer }
+}

+ 27 - 0
test/transition/karma.conf.js

@@ -0,0 +1,27 @@
+const featureFlags = require('../../scripts/feature-flags')
+process.env.CHROME_BIN = require('puppeteer').executablePath()
+
+const define = {
+  'process.env.CI': !!process.env.CI
+}
+
+for (const key in featureFlags) {
+  define[`process.env.${key}`] = featureFlags[key]
+}
+
+module.exports = function (config) {
+  config.set({
+    basePath: '.',
+    frameworks: ['jasmine'],
+    files: ['*.spec.ts'],
+    preprocessors: {
+      '*.spec.ts': ['esbuild']
+    },
+    esbuild: {
+      define
+    },
+    browsers: ['ChromeHeadless'],
+    plugins: ['karma-jasmine', 'karma-esbuild', 'karma-chrome-launcher'],
+    singleRun: true
+  })
+}

+ 1 - 0
test/transition/package.json

@@ -0,0 +1 @@
+{}

+ 8 - 9
test/unit/features/transition/transition-group.spec.ts → test/transition/transition-group.spec.ts

@@ -1,8 +1,7 @@
 import Vue from 'vue'
-import injectStyles from './inject-styles'
-import { nextFrame } from 'web/runtime/transition-util'
+import { injectStyles, waitForUpdate, nextFrame } from './helpers'
 
-describe.skip('Transition group', () => {
+describe('Transition group', () => {
   const { duration, buffer } = injectStyles()
 
   let el
@@ -212,9 +211,9 @@ describe.skip('Transition group', () => {
 
   it('events', (done) => {
     let next
-    const beforeEnterSpy = vi.fn()
-    const afterEnterSpy = vi.fn()
-    const afterLeaveSpy = vi.fn()
+    const beforeEnterSpy = jasmine.createSpy()
+    const afterEnterSpy = jasmine.createSpy()
+    const afterLeaveSpy = jasmine.createSpy()
     const vm = new Vue({
       template: `
           <div>
@@ -254,7 +253,7 @@ describe.skip('Transition group', () => {
           `<div class="test v-enter v-enter-active">d</div>` +
           `</span>`
       )
-      expect(beforeEnterSpy.mock.calls.length).toBe(1)
+      expect(beforeEnterSpy.calls.count()).toBe(1)
     })
       .thenWaitFor((_next) => {
         next = _next
@@ -268,7 +267,7 @@ describe.skip('Transition group', () => {
             `<div class="test">d</div>` +
             `</span>`
         )
-        expect(afterEnterSpy.mock.calls.length).toBe(1)
+        expect(afterEnterSpy.calls.count()).toBe(1)
         vm.items.shift()
       })
       .thenWaitFor((_next) => {
@@ -282,7 +281,7 @@ describe.skip('Transition group', () => {
             `<div class="test">d</div>` +
             `</span>`
         )
-        expect(afterLeaveSpy.mock.calls.length).toBe(1)
+        expect(afterLeaveSpy.calls.count()).toBe(1)
       })
       .then(done)
   })

+ 3 - 4
test/unit/features/transition/transition-mode.spec.ts → test/transition/transition-mode.spec.ts

@@ -1,8 +1,7 @@
 import Vue from 'vue'
-import injectStyles from './inject-styles'
-import { nextFrame } from 'web/runtime/transition-util'
+import { injectStyles, waitForUpdate, nextFrame } from './helpers'
 
-describe.skip('Transition mode', () => {
+describe('Transition mode', () => {
   const { duration, buffer } = injectStyles()
   const components = {
     one: { template: '<div>one</div>' },
@@ -667,7 +666,7 @@ describe.skip('Transition mode', () => {
           expect(vm.$el.children[0].className).toMatch('test-anim-leave-active')
           expect(vm.$el.children[1].className).toBe('')
         })
-        .thenWaitFor(duration + buffer)
+        .thenWaitFor(duration + buffer * 2)
         .then(() => {
           expect(vm.$el.children.length).toBe(1)
           expect(vm.$el.textContent).toBe('component B')

+ 19 - 20
test/unit/features/transition/transition-with-keep-alive.spec.ts → test/transition/transition-with-keep-alive.spec.ts

@@ -1,27 +1,26 @@
 import Vue from 'vue'
-import injectStyles from './inject-styles'
-import { nextFrame } from 'web/runtime/transition-util'
+import { injectStyles, waitForUpdate, nextFrame } from './helpers'
 
-describe.skip('Transition w/ KeepAlive', () => {
+describe('Transition w/ KeepAlive', () => {
   const { duration, buffer } = injectStyles()
 
   let components, one, two, el
   beforeEach(() => {
     one = {
       template: '<div>one</div>',
-      created: vi.fn(),
-      mounted: vi.fn(),
-      activated: vi.fn(),
-      deactivated: vi.fn(),
-      destroyed: vi.fn()
+      created: jasmine.createSpy(),
+      mounted: jasmine.createSpy(),
+      activated: jasmine.createSpy(),
+      deactivated: jasmine.createSpy(),
+      destroyed: jasmine.createSpy()
     }
     two = {
       template: '<div>two</div>',
-      created: vi.fn(),
-      mounted: vi.fn(),
-      activated: vi.fn(),
-      deactivated: vi.fn(),
-      destroyed: vi.fn()
+      created: jasmine.createSpy(),
+      mounted: jasmine.createSpy(),
+      activated: jasmine.createSpy(),
+      deactivated: jasmine.createSpy(),
+      destroyed: jasmine.createSpy()
     }
     components = {
       one,
@@ -33,11 +32,11 @@ describe.skip('Transition w/ KeepAlive', () => {
 
   function assertHookCalls(component, callCounts) {
     expect([
-      component.created.mock.calls.length,
-      component.mounted.mock.calls.length,
-      component.activated.mock.calls.length,
-      component.deactivated.mock.calls.length,
-      component.destroyed.mock.calls.length
+      component.created.calls.count(),
+      component.mounted.calls.count(),
+      component.activated.calls.count(),
+      component.deactivated.calls.count(),
+      component.destroyed.calls.count()
     ]).toEqual(callCounts)
   }
 
@@ -515,7 +514,7 @@ describe.skip('Transition w/ KeepAlive', () => {
   })
 
   it('async components with transition-mode out-in', (done) => {
-    const barResolve = vi.fn()
+    const barResolve = jasmine.createSpy()
     let next
     const foo = (resolve) => {
       setTimeout(() => {
@@ -591,7 +590,7 @@ describe.skip('Transition w/ KeepAlive', () => {
         .then(() => {
           // foo afterLeave get called
           // and bar has already been resolved before afterLeave get called
-          expect(barResolve.mock.calls.length).toBe(1)
+          expect(barResolve.calls.count()).toBe(1)
           expect(vm.$el.innerHTML).toBe('<!---->')
         })
         .thenWaitFor(nextFrame)

+ 23 - 24
test/unit/features/transition/transition.spec.ts → test/transition/transition.spec.ts

@@ -1,8 +1,7 @@
 import Vue from 'vue'
-import injectStyles from './inject-styles'
-import { nextFrame } from 'web/runtime/transition-util'
+import { injectStyles, waitForUpdate, nextFrame } from './helpers'
 
-describe.skip('Transition basic', () => {
+describe('Transition basic', () => {
   const { duration, buffer } = injectStyles() as {
     duration: number
     buffer: number
@@ -202,8 +201,8 @@ describe.skip('Transition basic', () => {
   })
 
   it('inline transition object', (done) => {
-    const enter = vi.fn()
-    const leave = vi.fn()
+    const enter = jasmine.createSpy()
+    const leave = jasmine.createSpy()
     const vm = new Vue({
       render(h) {
         return h('div', null, [
@@ -263,12 +262,12 @@ describe.skip('Transition basic', () => {
   })
 
   it('transition events', (done) => {
-    const onLeaveSpy = vi.fn()
-    const onEnterSpy = vi.fn()
-    const beforeLeaveSpy = vi.fn()
-    const beforeEnterSpy = vi.fn()
-    const afterLeaveSpy = vi.fn()
-    const afterEnterSpy = vi.fn()
+    const onLeaveSpy = jasmine.createSpy()
+    const onEnterSpy = jasmine.createSpy()
+    const beforeLeaveSpy = jasmine.createSpy()
+    const beforeEnterSpy = jasmine.createSpy()
+    const afterLeaveSpy = jasmine.createSpy()
+    const afterEnterSpy = jasmine.createSpy()
 
     const vm = new Vue({
       template: `
@@ -356,12 +355,12 @@ describe.skip('Transition basic', () => {
   })
 
   it('transition events (v-show)', (done) => {
-    const onLeaveSpy = vi.fn()
-    const onEnterSpy = vi.fn()
-    const beforeLeaveSpy = vi.fn()
-    const beforeEnterSpy = vi.fn()
-    const afterLeaveSpy = vi.fn()
-    const afterEnterSpy = vi.fn()
+    const onLeaveSpy = jasmine.createSpy()
+    const onEnterSpy = jasmine.createSpy()
+    const beforeLeaveSpy = jasmine.createSpy()
+    const beforeEnterSpy = jasmine.createSpy()
+    const afterLeaveSpy = jasmine.createSpy()
+    const afterEnterSpy = jasmine.createSpy()
 
     const vm = new Vue({
       template: `
@@ -524,8 +523,8 @@ describe.skip('Transition basic', () => {
   })
 
   it('css: false', (done) => {
-    const enterSpy = vi.fn()
-    const leaveSpy = vi.fn()
+    const enterSpy = jasmine.createSpy()
+    const leaveSpy = jasmine.createSpy()
     const vm = new Vue({
       template: `
           <div>
@@ -555,8 +554,8 @@ describe.skip('Transition basic', () => {
   })
 
   it('no transition detected', (done) => {
-    const enterSpy = vi.fn()
-    const leaveSpy = vi.fn()
+    const enterSpy = jasmine.createSpy()
+    const leaveSpy = jasmine.createSpy()
     const vm = new Vue({
       template:
         '<div><transition name="nope" @enter="enter" @leave="leave"><div v-if="ok">foo</div></transition></div>',
@@ -593,7 +592,7 @@ describe.skip('Transition basic', () => {
   })
 
   it('enterCancelled', (done) => {
-    const spy = vi.fn()
+    const spy = jasmine.createSpy()
     const vm = new Vue({
       template: `
           <div>
@@ -645,7 +644,7 @@ describe.skip('Transition basic', () => {
   })
 
   it('should remove stale leaving elements', (done) => {
-    const spy = vi.fn()
+    const spy = jasmine.createSpy()
     const vm = new Vue({
       template: `
           <div>
@@ -798,7 +797,7 @@ describe.skip('Transition basic', () => {
   })
 
   it('leaveCancelled (v-show only)', (done) => {
-    const spy = vi.fn()
+    const spy = jasmine.createSpy()
     const vm = new Vue({
       template: `
           <div>

+ 0 - 64
test/unit/features/transition/inject-styles.ts

@@ -1,64 +0,0 @@
-function insertCSS (text) {
-  const cssEl = document.createElement('style')
-  cssEl.textContent = text.trim()
-  document.head.appendChild(cssEl)
-}
-
-const duration = parseInt(process.env.TRANSITION_DURATION || '50')
-const buffer = parseInt(process.env.TRANSITION_BUFFER || '10')
-let injected = false
-
-export default function injectStyles () {
-  if (injected) return { duration, buffer }
-  injected = true
-  insertCSS(`
-    .test {
-      -webkit-transition: opacity ${duration}ms ease;
-      transition: opacity ${duration}ms ease;
-    }
-    .group-move {
-      -webkit-transition: -webkit-transform ${duration}ms ease;
-      transition: transform ${duration}ms ease;
-    }
-    .v-appear, .v-enter, .v-leave-active,
-    .test-appear, .test-enter, .test-leave-active,
-    .hello, .bye.active,
-    .changed-enter {
-      opacity: 0;
-    }
-    .test-anim-enter-active {
-      animation: test-enter ${duration}ms;
-      -webkit-animation: test-enter ${duration}ms;
-    }
-    .test-anim-leave-active {
-      animation: test-leave ${duration}ms;
-      -webkit-animation: test-leave ${duration}ms;
-    }
-    .test-anim-long-enter-active {
-      animation: test-enter ${duration * 2}ms;
-      -webkit-animation: test-enter ${duration * 2}ms;
-    }
-    .test-anim-long-leave-active {
-      animation: test-leave ${duration * 2}ms;
-      -webkit-animation: test-leave ${duration * 2}ms;
-    }
-    @keyframes test-enter {
-      from { opacity: 0 }
-      to { opacity: 1 }
-    }
-    @-webkit-keyframes test-enter {
-      from { opacity: 0 }
-      to { opacity: 1 }
-    }
-    @keyframes test-leave {
-      from { opacity: 1 }
-      to { opacity: 0 }
-    }
-    @-webkit-keyframes test-leave {
-      from { opacity: 1 }
-      to { opacity: 0 }
-    }
-  `)
-  return { duration, buffer }
-}
-

+ 8 - 3
test/vitest.setup.ts

@@ -2,7 +2,12 @@ process.env.NEW_SLOT_SYNTAX = 'true'
 
 import './helpers/shim-done'
 import './helpers/to-have-warned'
-import './helpers/wait-for-update'
-import './helpers/trigger-event'
-import './helpers/vdom'
 import './helpers/classlist'
+
+import { waitForUpdate } from './helpers/wait-for-update'
+import { triggerEvent } from './helpers/trigger-event'
+import { createTextVNode } from './helpers/vdom'
+
+global.waitForUpdate = waitForUpdate
+global.triggerEvent = triggerEvent
+global.createTextVNode = createTextVNode