example.pl 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. use strict;
  2. use warnings;
  3. use Data::Dumper;
  4. use Playwright;
  5. use Try::Tiny;
  6. use Net::EmptyPort;
  7. use Carp::Always;
  8. use constant IS_WIN => $^O eq 'MSWin32';
  9. NORMAL: {
  10. my $handle = Playwright->new( debug => 1 );
  11. # Open a new chrome instance
  12. my $browser = $handle->launch( headless => 1, type => 'firefox' );
  13. my $process = $handle->server( browser => $browser, command => 'process' );
  14. print "Browser PID: ".$process->{pid}."\n";
  15. # Open a tab therein
  16. my $page = $browser->newPage({ videosPath => 'video', acceptDownloads => 1 });
  17. # Test the spec method
  18. print Dumper($page->spec(),$page);
  19. # Browser contexts don't exist until you open at least one page.
  20. # You'll need this to grab and set cookies.
  21. my ($context) = @{$browser->contexts()};
  22. # Load a URL in the tab
  23. my $res = $page->goto('http://troglodyne.net', { waitUntil => 'networkidle' });
  24. print Dumper($res->status(), $browser->version());
  25. # Put your hand in the jar
  26. my $cookies = $context->cookies();
  27. print Dumper($cookies);
  28. # Grab the main frame, in case this is a frameset
  29. my $frameset = $page->mainFrame();
  30. print Dumper($frameset->childFrames());
  31. # Run some JS
  32. my $fun = "
  33. var input = arguments[0];
  34. return {
  35. width: document.documentElement.clientWidth,
  36. height: document.documentElement.clientHeight,
  37. deviceScaleFactor: window.devicePixelRatio,
  38. arg: input
  39. };";
  40. my $result = $page->evaluate($fun, 'zippy');
  41. print Dumper($result);
  42. # Read the console
  43. $page->on('console',"return [...arguments]");
  44. my $promise = $page->waitForEvent('console');
  45. #XXX this *can* race
  46. sleep 1;
  47. $page->evaluate("console.log('hug')");
  48. my $console_log = $handle->await( $promise );
  49. print "Logged to console: '".$console_log->text()."'\n";
  50. # Use a selector to find which input is visible and type into it
  51. # Ideally you'd use a better selector to solve this problem, but this is just showing off
  52. my $inputs = $page->selectMulti('input');
  53. foreach my $input (@$inputs) {
  54. try {
  55. # Pretty much a brute-force approach here, again use a better pseudo-selector instead like :visible
  56. $input->fill('tickle', { timeout => 250 } );
  57. } catch {
  58. print "Element not visible, skipping...\n";
  59. }
  60. }
  61. # Said better selector
  62. my $actual_input = $page->select('input[name=like]');
  63. $actual_input->fill('whee');
  64. # Ensure we can grab the parent (convenience)
  65. print "Got Parent: ISA ".ref($actual_input->{parent})."\n";
  66. # Take screen of said element
  67. $actual_input->screenshot({ path => 'test.jpg' });
  68. # Fiddle with HIDs
  69. my $mouse = $page->mouse;
  70. $mouse->move( 0, 0 );
  71. my $keyboard = $page->keyboard();
  72. $keyboard->type('F12');
  73. # Start to do some more advanced actions with the page
  74. use FindBin;
  75. use Cwd qw{abs_path};
  76. my $pg = abs_path("$FindBin::Bin/at/test.html");
  77. # Handle dialogs on page start, and dialog after dialog
  78. # NOTE -- the 'load' event won't fire until the dialog is dismissed in some browsers
  79. $promise = $page->waitForEvent('dialog');
  80. $page->goto("file://$pg", { waitUntil => 'networkidle' });
  81. my $dlg = $handle->await($promise);
  82. $promise = $page->waitForEvent('dialog');
  83. $dlg->dismiss();
  84. $dlg = $handle->await($promise);
  85. $dlg->accept();
  86. # Download stuff -- note this requries acceptDownloads = true in the page open
  87. # NOTE -- the 'download' event fires unreliably, as not all browsers properly obey the 'download' property in hrefs.
  88. # Chrome, for example would choke here on an intermediate dialog.
  89. $promise = $page->waitForEvent('download');
  90. $page->select('#d-lo')->click();
  91. my $download = $handle->await( $promise );
  92. print "Download suggested filename\n";
  93. print $download->suggestedFilename()."\n";
  94. $download->saveAs('test2.jpg');
  95. # Fiddle with file inputs
  96. my $choochoo = $page->waitForEvent('filechooser');
  97. $page->select('#drphil')->click();
  98. my $chooseu = $handle->await( $choochoo );
  99. $chooseu->setFiles('test.jpg');
  100. # Make sure we can do child selectors
  101. my $parent = $page->select('body');
  102. my $child = $parent->select('#drphil');
  103. print ref($child)."\n";
  104. # Test out pusht/popt/try_until
  105. # Timeouts are in milliseconds
  106. Playwright::pusht($page,5000);
  107. my $checkpoint = time();
  108. my $element = Playwright::try_until($page, 'select', 'bogus-bogus-nothere');
  109. my $elapsed = time() - $checkpoint;
  110. Playwright::popt($page);
  111. print "Waited $elapsed seconds for timeout to drop\n";
  112. $checkpoint = time();
  113. $element = Playwright::try_until($page, 'select', 'bogus-bogus-nothere');
  114. $elapsed = time() - $checkpoint;
  115. print "Waited $elapsed seconds for timeout to drop\n";
  116. # Try out the API testing extensions
  117. print "HEAD http://troglodyne.net : \n";
  118. my $fr = $page->request;
  119. my $resp = $fr->fetch("http://troglodyne.net", { method => "HEAD" });
  120. print Dumper($resp->headers());
  121. print "200 OK\n" if $resp->status() == 200;
  122. # Test that we can do stuff with with the new locator API.
  123. my $loc = $page->locator('body');
  124. my $innerTubes = $loc->allInnerTexts();
  125. print Dumper($innerTubes);
  126. my $image = $page->getByAltText('picture');
  127. print Dumper($image);
  128. # Save a video now that we are done
  129. my $bideo = $page->video;
  130. # IT IS IMPORTANT TO CLOSE THE PAGE FIRST OR THIS WILL HANG!
  131. $page->close();
  132. my $vidpath = $bideo->saveAs('video/example.webm');
  133. }
  134. # Example of using persistent mode / remote hosts
  135. OPEN: {
  136. my $handle = Playwright->new( debug => 1 );
  137. my $handle2 = Playwright->new( debug => 1, host => 'localhost', port => $handle->{port} );
  138. my $browser = $handle2->launch( headless => 1, type => 'firefox' );
  139. my $process = $handle2->server( browser => $browser, command => 'process' );
  140. print "Browser PID: ".$process->{pid}."\n";
  141. }
  142. goto DONE if IS_WIN;
  143. # Example of connecting to remote CDP sessions
  144. CDP: {
  145. local $SIG{HUP} = 'IGNORE';
  146. sub kill_krom_and_die {
  147. my ($in, $msg) = @_;
  148. kill_krom($in);
  149. die $msg;
  150. }
  151. sub kill_krom {
  152. my ($in) = @_;
  153. kill HUP => -getpgrp();
  154. close $in;
  155. }
  156. my $port = Net::EmptyPort::empty_port();
  157. my $pid = fork // die("Could not fork");
  158. if (!$pid) {
  159. open(my $stdin, '|-', qq{chromium-browser --remote-debugging-port=$port --headless}) or die "Could not open chromium-browser to test!";
  160. print "Waiting for cdp server on port $port to come up...\n";
  161. Net::EmptyPort::wait_port( $port, 10 )
  162. or kill_krom_and_die($stdin, "Server never came up after 10s!");
  163. print "done\n";
  164. #XXX not clear that this doesn't want an http uri instead? idk
  165. my $handle = Playwright->new( debug => 1, cdp_uri => "ws://127.0.0.1:$port" );
  166. # Open a new chrome instance
  167. my $browser = $handle->launch( headless => 1, type => 'chrome' );
  168. # Open a tab therein
  169. my $page = $browser->newPage({ videosPath => 'video', acceptDownloads => 1 });
  170. # Load a URL in the tab
  171. my $res = $page->goto('http://troglodyne.net', { waitUntil => 'networkidle' });
  172. print Dumper($res->status(), $browser->version());
  173. $handle->quit();
  174. #XXX OF COURSE chrome responds correctly to ESPIPE and SIGCHLD, why wouldn't it
  175. kill_krom($stdin);
  176. exit 0;
  177. } else {
  178. # If it can't get done in 20s, it ain't getting done
  179. foreach (0..20) {
  180. last unless waitpid( $pid, 1) == 0;
  181. sleep 1;
  182. }
  183. }
  184. print "All Done!\n\n";
  185. }
  186. # Clean up, since we left survivors
  187. require './bin/reap_playwright_servers';
  188. Playwright::ServerReaper::main();
  189. DONE:
  190. 0;