playwright-ruby-client 1.14.beta3 → 1.15.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -14
  3. data/documentation/docs/api/browser.md +4 -0
  4. data/documentation/docs/api/browser_context.md +5 -1
  5. data/documentation/docs/api/browser_type.md +2 -0
  6. data/documentation/docs/api/element_handle.md +30 -5
  7. data/documentation/docs/api/experimental/android_device.md +2 -0
  8. data/documentation/docs/api/frame.md +32 -6
  9. data/documentation/docs/api/locator.md +67 -38
  10. data/documentation/docs/api/mouse.md +11 -0
  11. data/documentation/docs/api/page.md +38 -7
  12. data/documentation/docs/api/request.md +34 -1
  13. data/documentation/docs/api/response.md +37 -2
  14. data/documentation/docs/api/selectors.md +29 -3
  15. data/documentation/docs/api/tracing.md +42 -8
  16. data/documentation/docs/api/worker.md +12 -11
  17. data/documentation/docs/article/getting_started.md +10 -1
  18. data/documentation/docs/article/guides/download_playwright_driver.md +9 -0
  19. data/documentation/docs/article/guides/inspector.md +1 -1
  20. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +56 -3
  21. data/documentation/docs/article/guides/rails_integration.md +4 -2
  22. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +86 -0
  23. data/documentation/docs/article/guides/recording_video.md +1 -1
  24. data/documentation/docs/article/guides/semi_automation.md +2 -2
  25. data/documentation/docs/article/guides/use_storage_state.md +78 -0
  26. data/documentation/docs/include/api_coverage.md +15 -0
  27. data/documentation/docusaurus.config.js +1 -0
  28. data/documentation/package.json +2 -2
  29. data/documentation/src/pages/index.js +0 -1
  30. data/documentation/static/img/playwright-ruby-client.png +0 -0
  31. data/documentation/yarn.lock +625 -549
  32. data/lib/playwright/channel.rb +36 -2
  33. data/lib/playwright/channel_owners/artifact.rb +6 -2
  34. data/lib/playwright/channel_owners/browser.rb +4 -0
  35. data/lib/playwright/channel_owners/browser_context.rb +21 -14
  36. data/lib/playwright/channel_owners/browser_type.rb +0 -1
  37. data/lib/playwright/channel_owners/element_handle.rb +10 -2
  38. data/lib/playwright/channel_owners/frame.rb +8 -0
  39. data/lib/playwright/channel_owners/page.rb +20 -4
  40. data/lib/playwright/channel_owners/playwright.rb +9 -0
  41. data/lib/playwright/channel_owners/request.rb +53 -17
  42. data/lib/playwright/channel_owners/response.rb +48 -5
  43. data/lib/playwright/connection.rb +8 -11
  44. data/lib/playwright/http_headers.rb +0 -6
  45. data/lib/playwright/locator_impl.rb +8 -0
  46. data/lib/playwright/mouse_impl.rb +9 -0
  47. data/lib/playwright/raw_headers.rb +61 -0
  48. data/lib/playwright/{route_handler_entry.rb → route_handler.rb} +30 -2
  49. data/lib/playwright/tracing_impl.rb +18 -7
  50. data/lib/playwright/transport.rb +2 -0
  51. data/lib/playwright/utils.rb +8 -1
  52. data/lib/playwright/version.rb +2 -2
  53. data/lib/playwright/web_socket_transport.rb +2 -0
  54. data/lib/playwright.rb +46 -5
  55. data/lib/playwright_api/android.rb +6 -6
  56. data/lib/playwright_api/android_device.rb +11 -9
  57. data/lib/playwright_api/browser.rb +12 -8
  58. data/lib/playwright_api/browser_context.rb +12 -8
  59. data/lib/playwright_api/browser_type.rb +9 -7
  60. data/lib/playwright_api/cdp_session.rb +7 -7
  61. data/lib/playwright_api/console_message.rb +6 -6
  62. data/lib/playwright_api/dialog.rb +6 -6
  63. data/lib/playwright_api/element_handle.rb +31 -8
  64. data/lib/playwright_api/frame.rb +35 -12
  65. data/lib/playwright_api/js_handle.rb +6 -6
  66. data/lib/playwright_api/locator.rb +39 -0
  67. data/lib/playwright_api/mouse.rb +8 -0
  68. data/lib/playwright_api/page.rb +43 -15
  69. data/lib/playwright_api/playwright.rb +6 -6
  70. data/lib/playwright_api/request.rb +33 -7
  71. data/lib/playwright_api/response.rb +31 -8
  72. data/lib/playwright_api/route.rb +6 -6
  73. data/lib/playwright_api/selectors.rb +38 -7
  74. data/lib/playwright_api/tracing.rb +33 -4
  75. data/lib/playwright_api/web_socket.rb +6 -6
  76. data/lib/playwright_api/worker.rb +8 -8
  77. metadata +8 -4
@@ -66,3 +66,14 @@ def up(button: nil, clickCount: nil)
66
66
  ```
67
67
 
68
68
  Dispatches a `mouseup` event.
69
+
70
+ ## wheel
71
+
72
+ ```
73
+ def wheel(deltaX, deltaY)
74
+ ```
75
+
76
+ Dispatches a `wheel` event.
77
+
78
+ > NOTE: Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling to
79
+ finish before returning.
@@ -281,7 +281,7 @@ def drag_and_drop(
281
281
  ## emulate_media
282
282
 
283
283
  ```
284
- def emulate_media(colorScheme: nil, media: nil, reducedMotion: nil)
284
+ def emulate_media(colorScheme: nil, forcedColors: nil, media: nil, reducedMotion: nil)
285
285
  ```
286
286
 
287
287
  This method changes the `CSS media type` through the `media` argument, and/or the `'prefers-colors-scheme'` media
@@ -622,18 +622,18 @@ def goto(url, referer: nil, timeout: nil, waitUntil: nil)
622
622
  Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
623
623
  last redirect.
624
624
 
625
- `page.goto` will throw an error if:
625
+ The method will throw an error if:
626
626
  - there's an SSL error (e.g. in case of self-signed certificates).
627
627
  - target URL is invalid.
628
628
  - the `timeout` is exceeded during navigation.
629
629
  - the remote server does not respond or is unreachable.
630
630
  - the main resource failed to load.
631
631
 
632
- `page.goto` will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not
632
+ The method will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not
633
633
  Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling
634
634
  [Response#status](./response#status).
635
635
 
636
- > NOTE: `page.goto` either throws an error or returns a main resource response. The only exceptions are navigation to
636
+ > NOTE: The method either throws an error or returns a main resource response. The only exceptions are navigation to
637
637
  `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
638
638
  > NOTE: Headless mode doesn't support navigation to a PDF document. See the
639
639
  [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
@@ -758,8 +758,6 @@ The method returns an element locator that can be used to perform actions on the
758
758
  element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
759
759
  different DOM elements. That would happen if the DOM structure between those actions has changed.
760
760
 
761
- Note that locator always implies visibility, so it will always be locating visible elements.
762
-
763
761
  Shortcut for main frame's [Frame#locator](./frame#locator).
764
762
 
765
763
  ## main_frame
@@ -935,7 +933,7 @@ last redirect.
935
933
  ## route
936
934
 
937
935
  ```
938
- def route(url, handler)
936
+ def route(url, handler, times: nil)
939
937
  ```
940
938
 
941
939
  Routing provides the capability to modify network requests that are made by a page.
@@ -943,6 +941,9 @@ Routing provides the capability to modify network requests that are made by a pa
943
941
  Once routing is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
944
942
 
945
943
  > NOTE: The handler will only be called for the first url if the response is a redirect.
944
+ > NOTE: [Page#route](./page#route) will not intercept requests intercepted by Service Worker. See
945
+ [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
946
+ request interception. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
946
947
 
947
948
  An example of a naive handler that aborts all image requests:
948
949
 
@@ -1032,6 +1033,36 @@ page.select_option("select#colors", value: ["red", "green", "blue"])
1032
1033
 
1033
1034
  Shortcut for main frame's [Frame#select_option](./frame#select_option).
1034
1035
 
1036
+ ## set_checked
1037
+
1038
+ ```
1039
+ def set_checked(
1040
+ selector,
1041
+ checked,
1042
+ force: nil,
1043
+ noWaitAfter: nil,
1044
+ position: nil,
1045
+ strict: nil,
1046
+ timeout: nil,
1047
+ trial: nil)
1048
+ ```
1049
+
1050
+ This method checks or unchecks an element matching `selector` by performing the following steps:
1051
+ 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
1052
+ 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.
1053
+ 1. If the element already has the right checked state, this method returns immediately.
1054
+ 1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If the
1055
+ element is detached during the checks, the whole action is retried.
1056
+ 1. Scroll the element into view if needed.
1057
+ 1. Use [Page#mouse](./page#mouse) to click in the center of the element.
1058
+ 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
1059
+ 1. Ensure that the element is now checked or unchecked. If not, this method throws.
1060
+
1061
+ When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
1062
+ zero timeout disables this.
1063
+
1064
+ Shortcut for main frame's [Frame#set_checked](./frame#set_checked).
1065
+
1035
1066
  ## set_content
1036
1067
 
1037
1068
  ```
@@ -18,6 +18,14 @@ complete with `'requestfinished'` event.
18
18
  If request gets a 'redirect' response, the request is successfully finished with the 'requestfinished' event, and a new
19
19
  request is issued to a redirected url.
20
20
 
21
+ ## all_headers
22
+
23
+ ```
24
+ def all_headers
25
+ ```
26
+
27
+ An object with all the request HTTP headers associated with this request. The header names are lower-cased.
28
+
21
29
  ## failure
22
30
 
23
31
  ```
@@ -48,7 +56,24 @@ Returns the [Frame](./frame) that initiated this request.
48
56
  def headers
49
57
  ```
50
58
 
51
- An object with HTTP headers associated with the request. All header names are lower-case.
59
+ **DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use [Request#all_headers](./request#all_headers) instead.
60
+
61
+ ## headers_array
62
+
63
+ ```
64
+ def headers_array
65
+ ```
66
+
67
+ An array with all the request HTTP headers associated with this request. Unlike [Request#all_headers](./request#all_headers), header
68
+ names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times.
69
+
70
+ ## header_value
71
+
72
+ ```
73
+ def header_value(name)
74
+ ```
75
+
76
+ Returns the value of the header matching the name. The name is case insensitive.
52
77
 
53
78
  ## navigation_request?
54
79
 
@@ -156,6 +181,14 @@ def response
156
181
 
157
182
  Returns the matching [Response](./response) object, or `null` if the response was not received due to error.
158
183
 
184
+ ## sizes
185
+
186
+ ```
187
+ def sizes
188
+ ```
189
+
190
+ Returns resource size information for given request.
191
+
159
192
  ## timing
160
193
 
161
194
  ```
@@ -6,6 +6,14 @@ sidebar_position: 10
6
6
 
7
7
  [Response](./response) class represents responses which are received by page.
8
8
 
9
+ ## all_headers
10
+
11
+ ```
12
+ def all_headers
13
+ ```
14
+
15
+ An object with all the response HTTP headers associated with this response.
16
+
9
17
  ## body
10
18
 
11
19
  ```
@@ -20,7 +28,7 @@ Returns the buffer with response body.
20
28
  def finished
21
29
  ```
22
30
 
23
- Waits for this response to finish, returns failure error if request failed.
31
+ Waits for this response to finish, returns always `null`.
24
32
 
25
33
  ## frame
26
34
 
@@ -36,7 +44,34 @@ Returns the [Frame](./frame) that initiated this response.
36
44
  def headers
37
45
  ```
38
46
 
39
- Returns the object with HTTP headers associated with the response. All header names are lower-case.
47
+ **DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use [Response#all_headers](./response#all_headers) instead.
48
+
49
+ ## headers_array
50
+
51
+ ```
52
+ def headers_array
53
+ ```
54
+
55
+ An array with all the request HTTP headers associated with this response. Unlike [Response#all_headers](./response#all_headers), header
56
+ names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times.
57
+
58
+ ## header_value
59
+
60
+ ```
61
+ def header_value(name)
62
+ ```
63
+
64
+ Returns the value of the header matching the name. The name is case insensitive. If multiple headers have the same name
65
+ (except `set-cookie`), they are returned as a list separated by `, `. For `set-cookie`, the `\n` separator is used. If
66
+ no headers are found, `null` is returned.
67
+
68
+ ## header_values
69
+
70
+ ```
71
+ def header_values(name)
72
+ ```
73
+
74
+ Returns all values of the headers matching the name, for example `set-cookie`. The name is case insensitive.
40
75
 
41
76
  ## json
42
77
 
@@ -15,9 +15,35 @@ def register(name, contentScript: nil, path: nil, script: nil)
15
15
 
16
16
  An example of registering selector engine that queries elements based on a tag name:
17
17
 
18
- ```python sync title=example_49f0cb9b5a21d0d5fe2b180c847bdb21068b335b4c2f42d5c05eb1957297899f.py
19
- # FIXME: add snippet
20
-
18
+ ```ruby
19
+ tag_selector = <<~JAVASCRIPT
20
+ {
21
+ // Returns the first element matching given selector in the root's subtree.
22
+ query(root, selector) {
23
+ return root.querySelector(selector);
24
+ },
25
+ // Returns all elements matching given selector in the root's subtree.
26
+ queryAll(root, selector) {
27
+ return Array.from(root.querySelectorAll(selector));
28
+ }
29
+ }
30
+ JAVASCRIPT
31
+
32
+ # Register the engine. Selectors will be prefixed with "tag=".
33
+ playwright.selectors.register("tag", script: tag_selector)
34
+ playwright.chromium.launch do |browser|
35
+ page = browser.new_page()
36
+ page.content = '<div><button>Click me</button></div>'
37
+
38
+ # Use the selector prefixed with its name.
39
+ button = page.query_selector('tag=button')
40
+ # Combine it with other selector engines.
41
+ page.click('tag=div >> text="Click me"')
42
+
43
+ # Can use it in any methods supporting selectors.
44
+ button_count = page.eval_on_selector_all('tag=button', 'buttons => buttons.length')
45
+ button_count # => 1
46
+ end
21
47
  ```
22
48
 
23
49
 
@@ -4,16 +4,15 @@ sidebar_position: 10
4
4
 
5
5
  # Tracing
6
6
 
7
- API for collecting and saving Playwright traces. Playwright traces can be opened using the Playwright CLI after
8
- Playwright script runs.
7
+ API for collecting and saving Playwright traces. Playwright traces can be opened in [Trace Viewer](https://playwright.dev/python/docs/trace-viewer)
8
+ after Playwright script runs.
9
9
 
10
- Start with specifying the folder traces will be stored in:
10
+ Start recording a trace before performing actions. At the end, stop tracing and save it to a file.
11
11
 
12
12
  ```ruby
13
- browser.new_page do |page|
14
- context = page.context
15
-
13
+ browser.new_context do |context|
16
14
  context.tracing.start(screenshots: true, snapshots: true)
15
+ page = context.new_page
17
16
  page.goto('https://playwright.dev')
18
17
  context.tracing.stop(path: 'trace.zip')
19
18
  end
@@ -30,15 +29,42 @@ def start(name: nil, screenshots: nil, snapshots: nil)
30
29
  Start tracing.
31
30
 
32
31
  ```ruby
33
- context = page.context
34
-
35
32
  context.tracing.start(name: 'trace', screenshots: true, snapshots: true)
33
+ page = context.new_page
36
34
  page.goto('https://playwright.dev')
37
35
  context.tracing.stop(path: 'trace.zip')
38
36
  ```
39
37
 
40
38
 
41
39
 
40
+ ## start_chunk
41
+
42
+ ```
43
+ def start_chunk
44
+ ```
45
+
46
+ Start a new trace chunk. If you'd like to record multiple traces on the same [BrowserContext](./browser_context), use
47
+ [Tracing#start](./tracing#start) once, and then create multiple trace chunks with [Tracing#start_chunk](./tracing#start_chunk) and
48
+ [Tracing#stop_chunk](./tracing#stop_chunk).
49
+
50
+ ```ruby
51
+ context.tracing.start(name: "trace", screenshots: true, snapshots: true)
52
+ page = context.new_page
53
+ page.goto("https://playwright.dev")
54
+
55
+ context.tracing.start_chunk
56
+ page.click("text=Get Started")
57
+ # Everything between start_chunk and stop_chunk will be recorded in the trace.
58
+ context.tracing.stop_chunk(path: "trace1.zip")
59
+
60
+ context.tracing.start_chunk
61
+ page.goto("http://example.com")
62
+ # Save a second trace file with different actions.
63
+ context.tracing.stop_chunk(path: "trace2.zip")
64
+ ```
65
+
66
+
67
+
42
68
  ## stop
43
69
 
44
70
  ```
@@ -46,3 +72,11 @@ def stop(path: nil)
46
72
  ```
47
73
 
48
74
  Stop tracing.
75
+
76
+ ## stop_chunk
77
+
78
+ ```
79
+ def stop_chunk(path: nil)
80
+ ```
81
+
82
+ Stop the trace chunk. See [Tracing#start_chunk](./tracing#start_chunk) for more details about multiple trace chunks.
@@ -8,17 +8,18 @@ The Worker class represents a [WebWorker](https://developer.mozilla.org/en-US/do
8
8
  event is emitted on the page object to signal a worker creation. `close` event is emitted on the worker object when the
9
9
  worker is gone.
10
10
 
11
- ```py title=example_29716fdd4471a97923a64eebeee96330ab508226a496ae8fd13f12eb07d55ee6.py
12
- def handle_worker(worker):
13
- print("worker created: " + worker.url)
14
- worker.on("close", lambda: print("worker destroyed: " + worker.url))
15
-
16
- page.on('worker', handle_worker)
17
-
18
- print("current workers:")
19
- for worker in page.workers:
20
- print(" " + worker.url)
21
-
11
+ ```ruby
12
+ def handle_worker(worker)
13
+ puts "worker created: #{worker.url}"
14
+ worker.once("close", -> (w) { puts "worker destroyed: #{w.url}" })
15
+ end
16
+
17
+ page.on('worker', method(:handle_worker))
18
+
19
+ puts "current workers:"
20
+ page.workers.each do |worker|
21
+ puts " #{worker.url}"
22
+ end
22
23
  ```
23
24
 
24
25
 
@@ -4,7 +4,14 @@ sidebar_position: 0
4
4
 
5
5
  # Getting started
6
6
 
7
- `playwright-ruby-client` doesn't include Playwright driver nor its downloader. **We have to install Playwright in advance**
7
+ ```
8
+ gem 'playwright-ruby-client'
9
+ ```
10
+
11
+ Add the line above and then `bundle install`.
12
+
13
+
14
+ Since `playwright-ruby-client` doesn't include Playwright driver nor its downloader, **we have to install Playwright in advance**
8
15
 
9
16
  ```shell
10
17
  $ npx playwright install
@@ -12,6 +19,8 @@ $ npx playwright install
12
19
 
13
20
  and then set `playwright_cli_executable_path: "npx playwright"` into `Playwright.create`.
14
21
 
22
+ Other methods of installation is also available. See the detail in [Download Playwright driver](./guides/download_playwright_driver)
23
+
15
24
  ## Enjoy with examples
16
25
 
17
26
  ### Capture a site
@@ -12,6 +12,15 @@ Choose any of the three ways as you prefer to download the driver:
12
12
  * `npm install`: the best choice for most use cases, with existing Node.js environment.
13
13
  * Direct download: maybe a good choice for Docker :whale: integration.
14
14
 
15
+ :::note
16
+
17
+ Also the article [Playwright on Alpine Linux](./playwright_on_alpine_linux) would be helpful if you plan to
18
+
19
+ * Build a browser server/container like Selenium Grid
20
+ * Run automation scripts on Alpine Linux
21
+
22
+ :::
23
+
15
24
  ## Using `npx`
16
25
 
17
26
  ```shell
@@ -1,5 +1,5 @@
1
1
  ---
2
- sidebar_position: 6
2
+ sidebar_position: 30
3
3
  ---
4
4
 
5
5
  # Playwright inspector
@@ -1,5 +1,5 @@
1
1
  ---
2
- sidebar_position: 7
2
+ sidebar_position: 40
3
3
  ---
4
4
 
5
5
  # Playwright on Alpine Linux
@@ -33,7 +33,23 @@ Playwright server is running on a container of [official Docker image](https://h
33
33
 
34
34
  ![overview](https://user-images.githubusercontent.com/11763113/124934448-ad4d0700-e03f-11eb-942e-b9f3282bb703.png)
35
35
 
36
- ## Playwright client
36
+ ### Playwright Server v.s. Browser Server
37
+
38
+ Playwright provides two kind of methods to share the browser environments for clients.
39
+
40
+ When you want to share only one browser environment, Browser server is suitable. This feature is officially supported in Playwright.
41
+
42
+ * Server can be launched with [BrowserType#launchServer](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server) instead of `BrowserType#launch`.
43
+ * Client can connect to server with [BrowserType#connect](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). In playwright-ruby-client, `BrowserType#connect` and not implemented yet and use `Playwright#connect_to_browser_server()` instead.
44
+
45
+ Another method is sharing all browser environment. This method is very simple, but not an official feature, and can be changed in future.
46
+
47
+ * Server can be launched with `playwright run-server` (CLI command).
48
+ * Client can connect to server with `Playwright.connect_to_playwright_server` instead of `Playwright.create`
49
+
50
+ ## Playwright server/client
51
+
52
+ ### Client code
37
53
 
38
54
  Many example uses `Playwright#create`, which internally uses Pipe (stdin/stdout) transport for Playwright-protocol messaging. Instead, **just use `Playwright#connect_to_playwright_server(endpoint)`** for WebSocket transport.
39
55
 
@@ -51,7 +67,7 @@ end
51
67
 
52
68
  `wss://example.com:8888/ws` is an example of endpoint URL of the Playwright server. In local development environment, it is typically `"ws://127.0.0.1:#{port}/ws"`.
53
69
 
54
- ## Playwright server
70
+ ### Server code
55
71
 
56
72
  With the [official Docker image](https://hub.docker.com/_/microsoft-playwright) or in the local development environment with Node.js, just execute `npx playwright install && npx playwright run-server $PORT`. (`$PORT` is a port number of the server)
57
73
 
@@ -67,6 +83,43 @@ ENV PORT 8888
67
83
  CMD ["./node_modules/.bin/playwright", "run-server", "$PORT"]
68
84
  ```
69
85
 
86
+ ## Browser server/client
87
+
88
+ ### Client code
89
+
90
+ Use `Playwright#connect_to_playwright_server` and pass the WebSocket URL for browser server.
91
+ Note that this method requires a block with `Browser`, not `Playwright` or `BrowserType`.
92
+
93
+ ```ruby
94
+ Playwright.connect_to_playwright_server(ws_url) do |browser|
95
+ page = browser.new_page
96
+ page.goto(...)
97
+ ...
98
+ end
99
+ ```
100
+
101
+ ### Server code
102
+
103
+ For instant use, `npx playwright launch-server chromium` generates a WebSocket endpoint URL with a random path.
104
+
105
+ More customization can be done by implementing JavaScript server like below:
106
+
107
+ ```js
108
+ const playwright = require('playwright')
109
+
110
+ option = {
111
+ channel: 'chrome-canary',
112
+ headless: false,
113
+ port: 8080,
114
+ wsPath: 'ws',
115
+ }
116
+ playwright.chromium.launchServer(option).then((server) => { console.log(server.wsEndpoint()) })
117
+ ```
118
+
119
+ `port` and `wsPath` would be useful for generating static WebSocket endpoint URL.
120
+ Other available options for `BrowserType#launchServer` can be found here:
121
+ https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server
122
+
70
123
  ## Debugging for connection
71
124
 
72
125
  The client and server are really quiet. This chapter shows how to check if the communication on the WebSocket works well or not.