George S. Baugh 2 years ago
parent
commit
f9be569c7f
9 changed files with 62 additions and 23 deletions
  1. 4 0
      conf/Changes
  2. 2 1
      dist.ini
  3. 7 0
      example.pl
  4. 2 2
      generate_perl_modules.pl
  5. 14 0
      lib/Playwright.pm
  6. 1 1
      lib/Playwright/Base.pm
  7. 1 1
      package.json
  8. 29 16
      playwright_server
  9. 2 2
      t/Playwright-Base.t

+ 4 - 0
conf/Changes

@@ -1,5 +1,9 @@
 Revision history for Playwright
 
+0.016 2021-09-16 TEODESIAN
+    - Prevent deep recursion due to playwright using the _request name for methods too
+    - Actually support getting FetchRequest objects via the _request() method
+
 0.015 2021-09-13 TEODESIAN
     - Add a note on proper usage of Download classes to the POD.
 

+ 2 - 1
dist.ini

@@ -1,5 +1,5 @@
 name = Playwright
-version = 0.015
+version = 0.016
 author = George S. Baugh <george@troglodyne.net>
 license = MIT
 copyright_holder = Troglodyne LLC
@@ -7,6 +7,7 @@ copyright_year = 2021
 
 [Run::BeforeBuild]
 run = npm i
+run = npm i playwright@latest
 run = sudo npx playwright install-deps
 run = ./generate_api_json.sh
 run = ./generate_perl_modules.pl

+ 7 - 0
example.pl

@@ -124,6 +124,13 @@ my $parent = $page->select('body');
 my $child = $parent->select('#drphil');
 print ref($child)."\n";
 
+# Try out the "experimental" API testing extensions (FetchRequests)
+print "HEAD http://google.com : \n";
+my $fr = $page->_request();
+my $resp = $fr->fetch("http://google.com", { method => "HEAD" });
+print Dumper($resp->headers());
+print "200 OK\n" if $resp->status() == 200;
+
 # Save a video now that we are done
 my $bideo = $page->video;
 

+ 2 - 2
generate_perl_modules.pl

@@ -71,7 +71,7 @@ our %bogus_methods = (
 );
 
 # Playwright methods we can't actually have work here
-our @banned = ('_request');
+our @banned = ('_api_request');
 
 my @modules;
 foreach my $class ( keys(%$spec), 'Mouse', 'Keyboard' ) {
@@ -102,7 +102,7 @@ See L<https://playwright.dev/api/class-$class#$class-$method> for more informati
 
 sub $renamed {
     my \$self = shift;
-    return \$self->_request(
+    return \$self->_api_request(
         args    => [\@_],
         command => '$method',
         object  => \$self->{guid},

+ 14 - 0
lib/Playwright.pm

@@ -229,6 +229,20 @@ Remember when doing an await() with playwright-perl you are waiting on a remote
 You may wish to spawn a subprocess using a different tool to download very large files.
 If this is not an option, consider increasing the timeout on the LWP object used by the Playwright object (it's the 'ua' member of the class).
 
+=head2 Doing arbitrary requests
+
+When you either want to test APIs (or not look like a scraper/crawler) you'll want to issue arbitrary requests, such as POST/HEAD/DELETE et cetera.
+Here's how you go about that:
+
+    print "HEAD http://google.com : \n";
+    my $fr = $page->_request();
+    my $resp = $fr->fetch("http://google.com", { method => "HEAD" });
+    print Dumper($resp->headers());
+    print "200 OK\n" if $resp->status() == 200;
+
+The _request() method will give you a Playwright::FetchRequest object, which you can then call whichever methods you like upon.
+When you call fetch (or get, post, etc) you will then be returned a Playwright::FetchResponse object.
+
 =head1 INSTALLATION NOTE
 
 If you install this module from CPAN, you will likely encounter a croak() telling you to install node module dependencies.

+ 1 - 1
lib/Playwright/Base.pm

@@ -86,7 +86,7 @@ sub _coerce ( $spec, %args ) {
     return %args;
 }
 
-sub _request ( $self, %args ) {
+sub _api_request ( $self, %args ) {
 
     %args = Playwright::Base::_coerce( $self->{spec}, %args );
 

+ 1 - 1
package.json

@@ -6,7 +6,7 @@
   "private": true,
   "dependencies": {
     "express": "^4.17",
-    "playwright": "^1.5",
+    "playwright": "^1.15.1",
     "uuid": "^8.3"
   }
 }

+ 29 - 16
playwright_server

@@ -139,7 +139,9 @@ app.post('/server', async (req, res) => {
     var object  = payload.object;
     var command = payload.command;
     var result = { error : true, message : "Please pass a valid browser object ID. got:", object };
-    console.log(object,command);
+    if (debug) {
+        console.log(object,command);
+    }
     if (objects[object]) {
         var msg = objects[object].server[command](...args);
         result = { error : false, message : msg };
@@ -187,33 +189,44 @@ app.post('/command', async (req, res) => {
                     ...args
                 ];
             }
-            const res = await subject[command](...args);
-            result = { error : false, message : res };
+            var commandResult;
+            if (command == '_request') {
+                //TODO extend this to other attribute fetches as well in the future
+                commandResult = subject[command];
+            } else {
+                commandResult = await subject[command](...args);
+            }
+            result = { error : false, message : commandResult };
 
-            if (Array.isArray(res)) {
-                for (var r of res) {
+            if (Array.isArray(commandResult)) {
+                for (var r of commandResult) {
                     objects[r._guid] = r;
                 }
             }
 
             // XXX videos are special, we have to magic up a guid etc for them
-            if (command == 'video' && res) {
-                res._guid = 'Video@' + uuidv4();
-                res._type = 'Video';
+            if (command == 'video' && commandResult) {
+                commandResult._guid = 'Video@' + uuidv4();
+                commandResult._type = 'Video';
             }
             // XXX So are FileChooser object unfortunately
-            if (args[0] == 'filechooser' && res) {
-                res._guid = 'FileChooser@' + uuidv4();
-                res._type = 'FileChooser';
+            if (args[0] == 'filechooser' && commandResult) {
+                commandResult._guid = 'FileChooser@' + uuidv4();
+                commandResult._type = 'FileChooser';
             }
             // XXX Downloads too sigh
-            if (command == 'waitForEvent' && res._artifact) {
-                res._guid = 'Download@' + uuidv4();
-                res._type = 'Download';
+            if (command == 'waitForEvent' && commandResult._artifact) {
+                commandResult._guid = 'Download@' + uuidv4();
+                commandResult._type = 'Download';
+            }
+            // XXX I think you are starting to see a pattern here
+            if (commandResult && commandResult._initializer && commandResult._initializer.fetchUid) {
+                commandResult._guid = 'FetchResponse@' + uuidv4();
+                commandResult._type = 'FetchResponse';
             }
 
-            if (res && res._guid) {
-                objects[res._guid] = res;
+            if (commandResult && commandResult._guid) {
+                objects[commandResult._guid] = commandResult;
             }
         } catch (e) {
             result = { error : true, message : e.message };

+ 2 - 2
t/Playwright-Base.t

@@ -81,7 +81,7 @@ $utilmock->redefine('request', sub {
     return $result;
 });
 
-is( $obj->_request(%in), $result, "Data directly returned when no _type or _guid");
+is( $obj->_api_request(%in), $result, "Data directly returned when no _type or _guid");
 $result = { _guid => 666, _type => 'Fake' };
 my $exp_obj = Playwright::Fake->new(
     id     => 666,
@@ -89,7 +89,7 @@ my $exp_obj = Playwright::Fake->new(
     spec   => $Playwright::spec->{Fake}{members},
     handle => $obj
 );
-my $oot = $obj->_request(%in);
+my $oot = $obj->_api_request(%in);
 
 is( $oot, $exp_obj, "Object returned when _type or _guid returned");