PageSourceSearch

https://testplane.io/assets/js/1fc3639a.2c831249.js

js testplane.io collected 2026-10-03 20:20:39 UTC 27,846 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunktestplane_docs=self.webpackChunktestplane_docs||[]).push([["3520"],{17018(e,n,s){s.r(n),s.d(n,{metadata:()=>i,default:()=>p,frontMatter:()=>d,contentTitle:()=>h,toc:()=>m,assets:()=>c});var i=JSON.parse('{"id":"migrations/how-to-upgrade-hermione-to-4","title":"Hermione v3 \u2192 v4","description":"This guide is relevant only for projects using hermione versions older than 4.x.","source":"@site/versioned_docs/version-v8/migrations/how-to-upgrade-hermione-to-4.mdx","sourceDirName":"migrations","slug":"/migrations/how-to-upgrade-hermione-to-4","permalink":"/docs/v8/migrations/how-to-upgrade-hermione-to-4","draft":false,"unlisted":false,"editUrl":"https://github.com/gemini-testing/testplane-docs/tree/master/versioned_docs/version-v8/migrations/how-to-upgrade-hermione-to-4.mdx","tags":[],"version":"v8","frontMatter":{},"sidebar":"mainSidebar","previous":{"title":"Hiding Scrollbars","permalink":"/docs/v8/guides/visual-checks-best-practices/hiding-scrollbars"},"next":{"title":"Hermione v4 \u2192 v5","permalink":"/docs/v8/migrations/how-to-upgrade-hermione-to-5"}}'),t=s(74848),r=s(28453),o=s(50773),a=s(57250),l=s(77444);let d={},h="Hermione v3 \u2192 v4",c={},m=[{value:"Why Should You Upgrade?",id:"why_to_upgrade",level:2},{value:"What Has Changed?",id:"what_is_new",level:2},{value:"Command API",id:"api_changes",level:3},{value:"async/await Instead of Chaining",id:"feature_async_await",level:4},{value:"Direct Result Instead of Object with value Key",id:"feature_direct_result",level:4},{value:"Direct Work with Elements",id:"feature_commands_of_elements",level:4},{value:"Passing Arguments Through an Object",id:"feature_passing_args_as_object",level:4},{value:"Special Command for React",id:"feature_commands_for_react",level:4},{value:"Up-to-Date Documentation",id:"feature_actual_docs",level:3},{value:"Tests Run Faster",id:"feature_running_tests_faster",level:3},{value:"Easy Local Browser Testing",id:"feature_running_tests_in_local_browser",level:3},{value:"API for Network Request Stubbing",id:"feature_api_to_mock_network",level:3},{value:"Browser Configuration in the Config",id:"browsers_config",level:3},{value:"How to Migrate?",id:"how_to_move",level:2},{value:"1. Update hermione to 4+, Install Migrator Plugin and Codemod",id:"update_hermione_and_install_plugin_and_codmode",level:3},{value:"2. Run the Codemod for async/await",id:"change_to_async_await_by_codmode",level:3},{value:"3. Run the Codemod to Remove value",id:"remove_value_by_codmode",level:3},{value:"4. Add hermione-wdio-migrator to the hermione Config",id:"add_migrator",level:3},{value:"5. Remove the Codemod",id:"remove_codemod",level:3},{value:"6. Run Linters",id:"run_linters",level:3},{value:"7. Run the Tests",id:"run_tests",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Support",id:"support",level:2}];function u(e){let n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.header,{children:(0,t.jsx)(n.h1,{id:"hermione-v3--v4",children:"Hermione v3 \u2192 v4"})}),"\n",(0,t.jsx)(l.A,{type:"warning",children:(0,t.jsx)(n.p,{children:"This guide is relevant only for projects using hermione versions older than 4.x."})}),"\n",(0,t.jsx)(n.h2,{id:"why_to_upgrade",children:"Why Should You Upgrade?"}),"\n",(0,t.jsxs)(n.p,{children:['A long time ago, hermione switched to a "temporary" fork of the ',(0,t.jsx)(n.a,{href:"https://webdriver.io",children:"webdriverio@4"}),' package (abbr. wdio) that it used "under the hood" because issues in the ',(0,t.jsx)(n.em,{children:"external"})," ",(0,t.jsx)(n.em,{children:"wdio"})," were slowing down its development: constant bugs in ",(0,t.jsx)(n.em,{children:"wdio"}),", disagreements over changes, etc. Initially, the fork was regularly updated by the hermione team to provide users with up-to-date functionality, but over time, the fork significantly lagged behind the current version of ",(0,t.jsx)(n.em,{children:"wdio"})," globally."]}),"\n",(0,t.jsxs)(n.p,{children:["By the time many new features appeared in ",(0,t.jsx)(n.em,{children:"wdio"})," that interested developers, such as ",(0,t.jsx)(n.a,{href:"../../guides/how-to-use-cdp",children:"Chrome DevTools Protocol (CDP)"}),", ",(0,t.jsx)(n.a,{href:"../../guides/how-to-intercept-requests-and-responses",children:"mocking external requests"}),", extended mobile device capabilities, etc., the hermione team had no choice but to abandon the ",(0,t.jsx)(n.em,{children:"webdriverio@4"})," fork and switch to the latest version of ",(0,t.jsx)(n.em,{children:"webdriverio@7"}),"."]}),"\n",(0,t.jsx)(l.A,{type:"info",children:(0,t.jsxs)(n.p,{children:["Version 8 of ",(0,t.jsx)(n.em,{children:"webdriverio"})," is already available, and ",(0,t.jsx)(n.em,{children:"hermione@7"})," uses it."]})}),"\n",(0,t.jsxs)(n.p,{children:["Additionally, users found it increasingly inconvenient to use outdated commands: typings had to be included from a separate package (in the new ",(0,t.jsx)(n.em,{children:"wdio"}),", they are included out of the box), and documentation for commands had to be accessed on the ",(0,t.jsx)(n.a,{href:"http://v4.webdriver.io/api.html",children:"old site"}),", while some users occasionally visited the ",(0,t.jsx)(n.a,{href:"https://webdriver.io/docs/api",children:"current page"})," and couldn't understand why the commands from the documentation didn't work in hermione."]}),"\n",(0,t.jsx)(n.p,{children:"Thus, there were plenty of reasons for a radical upgrade \u2014 jumping up by three major versions."}),"\n",(0,t.jsx)(n.h2,{id:"what_is_new",children:"What Has Changed?"}),"\n",(0,t.jsx)(n.p,{children:"There are many changes, so only the most important/interesting ones will be listed below."}),"\n",(0,t.jsx)(n.h3,{id:"api_changes",children:"Command API"}),"\n",(0,t.jsx)(n.h4,{id:"feature_async_await",children:"async/await Instead of Chaining"}),"\n",(0,t.jsxs)(n.p,{children:["In the new version, you can no longer write tests using ",(0,t.jsx)(n.em,{children:"chaining"}),". Only ",(0,t.jsx)(n.em,{children:"async/await"})," syntax is available:"]}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Old",label:"Old",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', function() {\
1n      return this.browser\n          .foo()\n          .bar()\n          .baz();\n  });\n"})})}),(0,t.jsx)(a.A,{value:"New",label:"New",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function() {\n      await this.browser.foo();\n      await this.browser.bar();\n      await this.browser.baz();\n  });\n"})})})]}),"\n",(0,t.jsxs)(n.p,{children:["Starting from version ",(0,t.jsx)(n.a,{href:"https://github.com/gemini-testing/hermione/blob/master/CHANGELOG.md#490-2022-05-24",children:"[email protected]"}),", you can write tests even shorter, as hermione now passes an object with a ",(0,t.jsx)(n.code,{children:"browser"})," field to the function:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:'it("some test", async function ({ browser }) {\n    await browser.foo();\n    await browser.bar();\n    await browser.baz();\n});\n'})}),"\n",(0,t.jsx)(l.A,{type:"warning",children:(0,t.jsxs)(n.p,{children:['In the "New" examples going forward, it is assumed that the hermione version is at least\n',(0,t.jsx)(n.em,{children:"4.9.0"}),". If you plan to use hermione ",(0,t.jsx)(n.em,{children:"4+"})," but at a lower version than ",(0,t.jsx)(n.em,{children:"4.9.0"}),", you should still\naccess the browser in tests through ",(0,t.jsx)(n.em,{children:"this"}),", for example: ",(0,t.jsx)(n.em,{children:"await\nthis.browser.getText('.selector')"}),"."]})}),"\n",(0,t.jsx)(n.h4,{id:"feature_direct_result",children:"Direct Result Instead of Object with value Key"}),"\n",(0,t.jsxs)(n.p,{children:["Now, instead of returning an object with a ",(0,t.jsx)(n.code,{children:"value"})," key, the actual result is directly returned from command results (old behavior often led to errors in tests):"]}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Old",label:"Old",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function() {\n      const { value } = await this.browser.getText('.selector');\n      console.log(value); // some text\n  });\n"})})}),(0,t.jsx)(a.A,{value:"New",label:"New",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function({ browser }) {\n      const text = await browser.getText('.selector');\n      console.log(text); // some text\n  });\n"})})})]}),"\n",(0,t.jsx)(n.h4,{id:"feature_commands_of_elements",children:"Direct Work with Elements"}),"\n",(0,t.jsxs)(n.p,{children:["Using the ",(0,t.jsx)(n.a,{href:"../../commands/browser/_dollar",children:(0,t.jsx)(n.code,{children:"browser.$"})})," command, you can get an instance of the found element and work with it in the test. This is convenient when you need to interact with an element more than once (the element won't be searched again on the page):"]}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Old",label:"Old",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function() {\n      await this.browser.clearElement('.input');\n      await this.browser.setValue('.input', 'text');\n  });\n"})})}),(0,t.jsx)(a.A,{value:"New",label:"New",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function({ browser }) {\n      const elem = await browser.$('.input');\n\n      await elem.clearElement();\n      await elem.setValue('text');\n\n});\n\n"})})})]}),"\n",(0,t.jsx)(n.p,{children:"Also see the commands:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"../../commands/browser/_dollardollar",children:(0,t.jsx)(n.code,{children:"browser.$$"})})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"../../commands/element/_dollar",children:(0,t.jsx)(n.code,{children:"element.$"})})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"../../commands/element/_dollardollar",children:(0,t.jsx)(n.code,{children:"element.$$"})})}),"\n"]}),"\n",(0,t.jsx)(n.h4,{id:"feature_passing_args_as_object",children:"Passing Arguments Through an Object"}),"\n",(0,t.jsxs)(n.p,{children:["For many commands, arguments are now passed using an object with understandable keys instead of sequentially passing arguments, which could be very confusing. For example, in the ",(0,t.jsx)(n.a,{href:"../../commands/element/waitForExist",children:"waitForExist"})," command, which previously accepted even boolean values as arguments:"]}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Old",label:"Old",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"it('some test', async function() {\
1n    await this.browser.waitForExist('.selector', 1000, true);\n});\n"})})}),(0,t.jsx)(a.A,{value:"New",label:"New",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function({ browser }) {\n      const elem = await browser.$('.selector');\n\n      await elem.waitForExist({\n          timeout: 1000,\n          interval: 500,\n          reverse: true,\n          timeoutMsg: 'still exists'\n      });\n\n});\n\n"})})})]}),"\n",(0,t.jsx)(n.h4,{id:"feature_commands_for_react",children:"Special Command for React"}),"\n",(0,t.jsxs)(n.p,{children:["A bonus for those who are already using React \u2014 now you can use the ",(0,t.jsx)(n.a,{href:"../../commands/browser/reactDollar",children:(0,t.jsx)(n.code,{children:"browser.react$"})})," and ",(0,t.jsx)(n.a,{href:"../../commands/browser/reactDollarDollar",children:(0,t.jsx)(n.code,{children:"browser.react$$"})})," commands to find specific react components on the page with certain states. Similar commands are available for elements as well \u2014 ",(0,t.jsx)(n.a,{href:"../../commands/element/reactDollar",children:(0,t.jsx)(n.code,{children:"element.react$"})})," and ",(0,t.jsx)(n.a,{href:"../../commands/element/reactDollarDollar",children:(0,t.jsx)(n.code,{children:"element.react$$"})}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["Also read ",(0,t.jsx)(n.a,{href:"https://webdriver.io/docs/selectors/#react-selectors",children:"the article on working with react components"})," on the ",(0,t.jsx)(n.em,{children:"webdriverio"})," website."]}),"\n",(0,t.jsx)(n.p,{children:"Example usage:"}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Old",label:"Old",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"it('some test', async function() {\n    // no special commands for working with react components :(\n});\n"})})}),(0,t.jsx)(a.A,{value:"New",label:"New",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  it('some test', async function({ browser }) {\n      const component = await browser.react$('MyComponent', {\n          props: { someProp: true },\n          state: 'some-state'\n      });\n\n      const result = await component.isDisplayed();\n\n});\n\n"})})})]}),"\n",(0,t.jsx)(n.h3,{id:"feature_actual_docs",children:"Up-to-Date Documentation"}),"\n",(0,t.jsxs)(n.p,{children:["While hermione used the old version of ",(0,t.jsx)(n.em,{children:"webdriverio@4"}),", users constantly had to be reminded that the documentation for all commands was located at a separate address: ",(0,t.jsx)(n.a,{href:"http://v4.webdriver.io/api.html",children:"v4.webdriver.io/api.html"}),". Now, descriptions of all ",(0,t.jsx)(n.em,{children:"webdriverio"})," commands used by hermione can be found at the standard address: ",(0,t.jsx)(n.a,{href:"https://webdriver.io/docs/api",children:"webdriver.io/docs/api"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["In addition, we ",(0,t.jsx)(n.a,{href:"../../commands/overview",children:"translated the descriptions of all commands into Russian"})," and adapted all usage examples to hermione, since ",(0,t.jsx)(n.em,{children:"webdriverio"})," uses its own runner and the examples in its documentation cannot be directly applied in hermione."]}),"\n",(0,t.jsx)(n.h3,{id:"feature_running_tests_faster",children:"Tests Run Faster"}),"\n",(0,t.jsx)(n.p,{children:"You might not notice this speedup when running several tests locally, but it will be very noticeable with a large number of tests. The new commands work about 15% faster (assuming you have stopped using the old commands)."}),"\n",(0,t.jsx)(n.h3,{id:"feature_running_tests_in_local_browser",children:"Easy Local Browser Testing"}),"\n",(0,t.jsxs)(n.p,{children:["Previously, to run tests locally in your browser, you had to start ",(0,t.jsx)(n.code,{children:"selenium-standalone"})," and specify a magical ",(0,t.jsx)(n.code,{children:"gridUrl"})," to make things work in hermione. Now, it's much simpler: in the config, just specify the ",(0,t.jsx)(n.code,{children:"automationProtocol"})," option with the value ",(0,t.jsx)(n.code,{children:"devtools"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"// hermione.conf.js\nmodule.exports = {\n  browsers: {\n      chrome: {\n          automationProtocol: 'devtools',\n          desiredCapabilities: {\n              // ...\n          }\n      }\n  },\n\n  // other hermione settings...\n};\n"})}),"\n",(0,t.jsx)(n.p,{children:"We also plan to add a separate button in the hermione GUI for switching to CDP mode to make it even easier."}),"\n",(0,t.jsx)(l.A,{type:"warning",children:(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Currently, this is fully supported only in the ",(0,t.jsx)(n.em,{children:"Chrome"})," browser. * Retaking screenshots in\nthis mode should only be done for debugging, as browsers in the pipeline run under ",(0,t.jsx)(n.em,{children:"Linux"}),",\nwhich means page rendering will differ and tests in the pull request will fail with diffs."]}),"\n"]})}),"\n",(0,t.jsx)(n.h3,{id:"feature_api_to_mock_network",children:"API for Network Request Stubbing"}),"\n",(0,t.jsxs)(n.p,{children:["The new version provides the ability to stub or override the responses of your service. This is done using the ",(0,t.jsx)(n.a,{href:"../../commands/mock/respond",children:"mock.respond()"})," command. You can also block URLs of external services."]}),"\n",(0,t.jsx)(n.p,{children:'Read more about all the features in the "How to Track and Intercept Network Requests and Responses" guide.'}),"\n",(0,t.jsx)(l.A,{type:"warning",children:(0,t.jsxs)(n.p,{children:["Currently, this functionality only w
1orks in ",(0,t.jsx)(n.em,{children:"Chrome DevTools Protocol (CDP)"})," mode, which only\nworks in ",(0,t.jsx)(n.em,{children:"Chrome"})," and ",(0,t.jsx)(n.em,{children:"Firefox Nightly"}),"."]})}),"\n",(0,t.jsx)(n.h3,{id:"browsers_config",children:"Browser Configuration in the Config"}),"\n",(0,t.jsxs)(n.p,{children:["For browsers that support the W3C protocol, instead of the ",(0,t.jsx)(n.code,{children:"version"})," field, you need to specify ",(0,t.jsx)(n.code,{children:"browserVersion"}),". And additional options need to be prefixed with the browser's name:"]}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Old",label:"Old",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  module.exports = {\n      browsers: {\n          'chrome-desktop': {\n              desiredCapabilities: {\n                  browserName: 'chrome',\n                  version: '75',\n                  'chromeOptions': {\n                      // ...\n                  }\n              }\n          }\n      }\n  };\n"})})}),(0,t.jsx)(a.A,{value:"New",label:"New",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"  module.exports = {\n      browsers: {\n          'chrome-desktop': {\n              desiredCapabilities: {\n                  browserName: 'chrome',\n                  browserVersion: '75',\n                  'goog:chromeOptions': { // for Chrome browser, prefix with 'goog'\n                      // ...\n                  }\n              }\n          }\n      }\n  };\n"})})})]}),"\n",(0,t.jsxs)(n.p,{children:["Read more about vendor prefixes at ",(0,t.jsx)(n.a,{href:"https://w3c.github.io/webdriver/#protocol-extensions",children:"this link"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["The list of all available settings can be found in the ",(0,t.jsx)(n.a,{href:"https://w3c.github.io/webdriver/#capabilities",children:"specification"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"how_to_move",children:"How to Migrate?"}),"\n",(0,t.jsxs)(n.p,{children:["We upgraded ",(0,t.jsx)(n.em,{children:"webdriverio"})," by three major versions at once, so simply updating the hermione version in ",(0,t.jsx)(n.code,{children:"package.json"})," won't suffice. The main issues during migration are the absent ",(0,t.jsx)(n.em,{children:"chaining"})," in tests and outdated test commands. To help you with these issues, we wrote the following guide."]}),"\n",(0,t.jsx)(n.h3,{id:"update_hermione_and_install_plugin_and_codmode",children:"1. Update hermione to 4+, Install Migrator Plugin and Codemod"}),"\n",(0,t.jsx)(n.p,{children:"Specifically:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Update hermione to hermione@4."}),"\n",(0,t.jsxs)(n.li,{children:["Install the ",(0,t.jsx)(n.a,{href:"https://github.com/gemini-testing/hermione-wdio-migrator",children:"hermione-wdio-migrator"})," plugin for smooth command migration."]}),"\n",(0,t.jsxs)(n.li,{children:["Install the ",(0,t.jsx)(n.a,{href:"https://github.com/gemini-testing/hermione-codemod",children:"hermione-codemod"})," package to convert existing tests to the new syntax."]}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"You can do all this with one command:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:"npm install -D hermione@4 hermione-wdio-migrator hermione-codemod --save-exact\n"})}),"\n",(0,t.jsxs)(n.p,{children:["The versions of all hermione plugins (e.g., ",(0,t.jsx)(n.a,{href:"../../html-reporter/html-reporter-setup",children:"html-reporter"}),") also need to be updated to the latest versions, as some may not work correctly with the new hermione version."]}),"\n",(0,t.jsx)(n.h3,{id:"change_to_async_await_by_codmode",children:"2. Run the Codemod for async/await"}),"\n",(0,t.jsxs)(n.p,{children:["The codemod will regenerate your tests from ",(0,t.jsx)(n.em,{children:"chaining"})," format to ",(0,t.jsx)(n.em,{children:"async/await"})," format:"]}),"\n",(0,t.jsxs)(o.A,{children:[(0,t.jsxs)(a.A,{value:"zsh",label:"zsh",children:[(0,t.jsxs)(n.p,{children:["If you use the ",(0,t.jsx)(n.em,{children:"zsh"})," shell, you can pass test files as relative paths and as glob patterns, such as ",(0,t.jsx)(n.em,{children:"somefolder/**/*.js"}),", and so on."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:"npx jscodeshift -t node_modules/hermione-codemod/transforms/browser-chaining-to-async-await.js path_to_file_mask\n"})})]}),(0,t.jsxs)(a.A,{value:"bash",label:"bash",children:[(0,t.jsxs)(n.p,{children:["If you use the ",(0,t.jsx)(n.em,{children:"bash"})," shell, globs won't work as easily as in ",(0,t.jsx)(n.em,{children:"zsh"}),", so the command will be more complex if you need to process a group of files. For example:"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:"npx jscodeshift -t node_modules/hermione-codemod/transforms/browser-chaining-to-async-await.js $(find ./somefolder -type f -iname '*.js' | xargs echo)\n"})})]})]}),"\n",(0,t.jsx)(n.p,{children:"Upon successful completion, you will see a corresponding message:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:"Results:\n0 errors\n0 unmodified\n0 skipped\n251 ok\n"})}),"\n",(0,t.jsx)(n.p,{children:"However, there can be cases the current codemod cannot handle. For such tests, an error with information about the problematic file will be displayed:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:'WARN: can\'t correctly transform ConditionalExpression, fix it manually\n      file: tests/hermione/suites/common/promotion-page/promotion-page.hermione.js\n      position: {"start":112,"end":116}\n'})}),"\n",(0,t.jsx)(n.p,{children:"Such tests will need to be fixed manually. We tried to account for most test cases, so there shouldn't be too many of these instances."}),"\n",(0,t.jsxs)(n.p,{children:["After this, you can ",(0,t.jsx)(n.strong,{children:"already"})," merge your changes (this is optional) to perform the migration in parts. The ",(0,t.jsx)(n.em,{children:"async/await"})," syntax will still work in ",(0,t.jsx)(n.em,{children:"wdio@4"}),". Many services have been writing tests this way for a long time."]}),"\n",(0,t.jsx)(n.h3,{id:"remove_value_by_codmode",children:"3. Run the C
1odemod to Remove value"}),"\n",(0,t.jsxs)(n.p,{children:["Since command results now directly return the actual result instead of an object with a ",(0,t.jsx)(n.code,{children:"value"})," key, you need to change result handling in all tests. This codemod is intended for such cases."]}),"\n",(0,t.jsx)(n.p,{children:"The command is very similar to the previous one, only the path to the next codemod file changes:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:"npx jscodeshift -t node_modules/hermione-codemod/transforms/remove-browser-prop.js path_to_file_mask\n"})}),"\n",(0,t.jsxs)(n.p,{children:["For any warnings, problematic tests need to be fixed manually. For example, if a test uses ",(0,t.jsx)(n.code,{children:"value"})," multiple times through destructuring, the codemod will not handle it properly and generic variable names might be generated. For example:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:"// test on wdio@4:\nit('test', function(){\n    return this.browser\n        ...\n        .getText('.button')\n        .then((value) => {\n            assert.equal(value, 'Button', 'We need a button');\n        })\n        ...\n        .getValue('.input')\n        .then((value) => {\n            assert.equal(value, 'Hello', 'We were not greeted');\n        });\n});\n\n// auto-generated for wdio@7 in this format:\nit('test', async function() {\n    const value = await this.browser.getText('.button');\n    assert.equal(value, 'Button', 'We need a button');\n    ...\n    // there will be an error due to reuse of the variable name,\n    // so the codemod will issue a warning about the problematic spot\n    const value = await this.browser.getValue('.input');\n    assert.equal(value, 'Hello', 'We were not greeted');\n});\n"})}),"\n",(0,t.jsxs)(n.p,{children:["If your project has too many tests that cannot be automatically migrated, please reach out to ",(0,t.jsx)(n.a,{href:"https://github.com/gemini-testing/testplane/issues/",children:"github issues"})," for help. We will analyze these errors and assist with the migration."]}),"\n",(0,t.jsx)(n.h3,{id:"add_migrator",children:"4. Add hermione-wdio-migrator to the hermione Config"}),"\n",(0,t.jsx)(n.p,{children:'This plugin "under the hood" simply adds the implementation of old commands using the new API, so during migration, you don\'t have to update tests yourself. Eventually, you should replace these deprecated commands with new ones in your tests:'}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-javascript",children:'module.exports = {\n    plugins: {\n        "hermione-wdio-migrator": {\n            enabled: true,\n        },\n\n        // other hermione plugins...\n    },\n\n    // other hermione settings...\n};\n'})}),"\n",(0,t.jsx)(n.h3,{id:"remove_codemod",children:"5. Remove the Codemod"}),"\n",(0,t.jsxs)(n.p,{children:["Uninstall the ",(0,t.jsx)(n.a,{href:"https://github.com/gemini-testing/hermione-codemod",children:"hermione-codemod"})," package as you won't need it anymore."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-shell",children:"npm uninstall hermione-codemod\n"})}),"\n",(0,t.jsx)(n.h3,{id:"run_linters",children:"6. Run Linters"}),"\n",(0,t.jsx)(n.p,{children:"Run linters on the modified tests, as the codemod might have violated your project's coding standards."}),"\n",(0,t.jsx)(n.h3,{id:"run_tests",children:"7. Run the Tests"}),"\n",(0,t.jsx)(n.p,{children:"Finally, ensure that all tests pass successfully by creating a pull request and verifying that all tests run successfully in CI."}),"\n",(0,t.jsx)(n.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,t.jsxs)(n.p,{children:["Updating ",(0,t.jsx)(n.em,{children:"webdriverio"})," brings many useful features that can be used right away, while some will appear in a more convenient form later. Therefore, we highly recommend upgrading to the new version to make writing tests more convenient, faster, and enjoyable."]}),"\n",(0,t.jsx)(l.A,{type:"warning",children:(0,t.jsx)(n.p,{children:"The old hermione version is in limited support mode, and new features will not appear in it."})}),"\n",(0,t.jsx)(n.h2,{id:"support",children:"Support"}),"\n",(0,t.jsxs)(n.p,{children:["If you encounter issues during the upgrade or have any questions, come to ",(0,t.jsx)(n.a,{href:"https://github.com/gemini-testing/testplane/issues/",children:"github issues"})," \u2014 we will definitely help you!"]})]})}function p(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(u,{...e})}):u(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.