From 23598e70db348fa2698c596942e0ee8d4172148c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Dobro=C5=88?= Date: Sun, 27 Mar 2022 23:19:00 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Initial=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 15 + .gitattributes | 11 + .gitignore | 7 + .php-cs-fixer.php | 48 +++ CHANGELOG.md | 10 + CONTRIBUTING.md | 32 ++ ISSUE_TEMPLATE.md | 27 ++ LICENSE | 21 ++ README.md | 184 +++++++++++ bigpipe.svg | 13 + composer.json | 47 +++ phpcs.xml.dist | 14 + phpunit.xml.dist | 20 ++ src/AsyncResponse.php | 291 ++++++++++++++++++ src/BigPipe.php | 81 +++++ .../BigPipeInvalidArgumentException.php | 7 + src/TransportMarker.php | 78 +++++ src/helpers.php | 12 + tests/AsyncResponseTest.php | 137 +++++++++ tests/TransportMarkersTest.php | 53 ++++ 20 files changed, 1108 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .php-cs-fixer.php create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 ISSUE_TEMPLATE.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bigpipe.svg create mode 100644 composer.json create mode 100644 phpcs.xml.dist create mode 100644 phpunit.xml.dist create mode 100644 src/AsyncResponse.php create mode 100644 src/BigPipe.php create mode 100644 src/Exceptions/BigPipeInvalidArgumentException.php create mode 100644 src/TransportMarker.php create mode 100644 src/helpers.php create mode 100644 tests/AsyncResponseTest.php create mode 100644 tests/TransportMarkersTest.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cd8eb86 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ed9aff4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Path-based git attributes +# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html + +# Ignore all test and documentation with "export-ignore". +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/ISSUE_TEMPLATE.md export-ignore +/phpcs.xml.dist export-ignore +/phpunit.xml.dist export-ignore +/tests export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a29932 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/.idea +/vendor +/.phpunit.cache +composer.lock +.php_cs.cache +.phpunit.result.cache +.php-cs-fixer.cache diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..24fefcb --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,48 @@ +setRules([ + '@PSR12' => true, + 'binary_operator_spaces' => true, + 'blank_line_after_opening_tag' => true, + 'compact_nullable_typehint' => true, + 'declare_equal_normalize' => true, + 'lowercase_cast' => true, + 'lowercase_static_reference' => true, + 'new_with_braces' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_leading_import_slash' => true, + 'no_whitespace_in_blank_line' => true, + 'ordered_class_elements' => [ + 'order' => [ + 'use_trait', + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', + 'function', + 'const', + ], + 'sort_algorithm' => 'none', + ], + 'return_type_declaration' => true, + 'short_scalar_cast' => true, + 'single_blank_line_before_namespace' => true, + 'single_trait_insert_per_statement' => true, + 'ternary_operator_spaces' => true, + 'unary_operator_spaces' => true, + 'visibility_required' => [ + 'elements' => [ + 'const', + 'method', + 'property', + ], + ], + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->exclude('vendor') + ->in([__DIR__.'/src/', __DIR__.'/tests/']) + ) + ; diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7060fd1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to `bigpipe-util` will be documented in this file. + +Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. + +## v0.1.1 - 2022-03-27 + +### Added +- Part of BigPipe implementation for Webpack. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4e004ce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +We accept contributions via Pull Requests on [Github](https://github.com/richardDobron/bigpipe-php). + + +## Pull Requests + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``. + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **Create feature branches** - Don't ask us to pull from your master branch. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + + +## Running Tests + +``` bash +$ composer test +``` + + +**Happy coding**! diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..5b48c57 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,27 @@ + + +## Detailed description + +Provide a detailed description of the change or addition you are proposing. + +Make it clear if the issue is a bug, an enhancement or just a question. + +## Context + +Why is this change important to you? How would you use it? + +How can it benefit other users? + +## Possible implementation + +Not obligatory, but suggest an idea for implementing addition or change. + +## Your environment + +Include as many relevant details about the environment you experienced the bug in and how to reproduce it. + +* Version used (e.g. PHP 5.6, HHVM 3): +* Operating system and version (e.g. Ubuntu 16.04, Windows 7): +* Link to your project: +* ... +* ... diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d5ce5cb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Richard Dobroň + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..860a097 --- /dev/null +++ b/README.md @@ -0,0 +1,184 @@ + + +This library currently implements small part of [Facebook BigPipe][blog] so far, but the advantage is to efficiently insert/replace content and work with the DOM. It is also possible to easily call JavaScript modules from PHP. + +## Demo App +Try the app with [live demo](http://bigpipe.xf.cz). + +## Requirements +* PHP 7.1 or higher +* Node 8, 10+. +* Webpack + +## Installation + +These steps are required: +1. Install composer package: +```shell +$ composer require richarddobron/bigpipe +``` + +2. Install npm package: +```shell +$ npm install bigpipe-util +``` + +3. Add this lines to /path/to/resources/js/app.js: +```javascript +import Primer from 'bigpipe-util/src/Primer'; + +Primer(); + +window.require = (modulePath) => { + return modulePath.startsWith('bigpipe-util/') + ? require('bigpipe-util/' + modulePath.substring(13) + '.js').default + : require('./' + modulePath).default; +}; +``` + +4. Create file /path/to/resources/js/ServerJS.js + - this step is optional, but if you skip it, use this in next step: + ```require("bigpipe-util/ServerJS")``` +```javascript +import ServerJSImpl from 'bigpipe-util/src/ServerJS'; +export default class ServerJS extends ServerJSImpl { +} +``` + +5. Add this lines to page footer: +```html + +``` + + +## DOMOPS API +- **setContent**: Sets the content of an element. +- **appendContent**: Insert content as the last child of specified element. +- **prependContent**: Insert content as the first child of specified element. +- **insertAfter**: Insert content after specified element. +- **insertBefore**: Insert content before specified element. +- **remove**: Remove specified element and its children. +- **replace**: Replace specified element with content. +- **eval**: Evaluates JavaScript code represented as a string. + +```php +$response = new \dobron\BigPipe\AsyncResponse(); + +$response->setContent('div#content', $newContent); +$response->send(); +``` + +## Refresh & Redirecting + +```php +$response = new \dobron\BigPipe\AsyncResponse(); + +$response->reload(250); // reload page with 250ms delay +// or +$response->redirect('/onboarding', 500); // redirect with 500ms delay + +$response->send(); +``` + +## Payload + +```php +$response = new \dobron\BigPipe\AsyncResponse(); + +$response->setPayload([ + 'username' => $_POST['username'], + 'status' => 'unavailable', + 'message' => 'Username is unavailable.', +]); + +$response->send(); +``` + +## BigPipe API +- **require**: Call JavaScript module method. You can call a specific class method or a regular function with the custom arguments. + +Example PHP code: +```php +$asyncResponse = new \dobron\BigPipe\AsyncResponse(); + +$asyncResponse->bigPipe()->require("require('SecretModule').run()", [ + 'first argument', + 'second argument', + ... +]); +$asyncResponse->send(); +``` +Example JavaScript code: +```javascript +class SecretModule { + run(first, second) { + // ... + } +} +``` +- **transport**: Through transport markers you can send HTML content but also transform the content into JavaScript objects (such as Map, Set or Element). + +Example PHP code: +```php +$asyncResponse = new \dobron\BigPipe\AsyncResponse(); +$asyncResponse->bigPipe()->require("require('Chart').setup()", [ + 'element' => \dobron\BigPipe\TransportMarker::transportElement('chart-div'), + 'dataPoints' => $asyncResponse->transport()->transportSet([ + ['x' => 10, 'y' => 71], + ['x' => 20, 'y' => 55], + ['x' => 30, 'y' => 50], + ['x' => 40, 'y' => 65], + ]), +]); +$asyncResponse->send(); +``` + +# What all can be Ajaxifed? + +## Links +```html +Remove Item +``` + +## Forms +```html +
+ + +
+``` + +## Dialogs +```html +Open Modal +``` + +## Inspiration + +BigPipe is inspired by the concept behind Facebook's BigPipe. For more details +read their blog post: [Pipelining web pages for high performance][blog]. + +## Motivation + +There is a large number of PHP projects for which moving to modern frameworks like Laravel Livewire, React, Vue.js (and many more!) could be very challenging. + +The purpose of this library is to rapidly reduce the continuously repetitive code to work with the DOM and improve the communication barrier between PHP and JavaScript. + +## Credits + +- [Richard Dobroň][link-author] + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. + +[link-author]: https://github.com/richardDobron +[blog]: https://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919 diff --git a/bigpipe.svg b/bigpipe.svg new file mode 100644 index 0000000..5f4a2d3 --- /dev/null +++ b/bigpipe.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b236005 --- /dev/null +++ b/composer.json @@ -0,0 +1,47 @@ +{ + "name": "richarddobron/bigpipe", + "description": "This library currently implements small part of Facebook BigPipe so far, but the advantage is to efficiently insert/replace content and work with the DOM. It is also possible to easily call JavaScript modules from PHP.", + "type": "library", + "keywords": [ + "bigpipe", + "xhr", + "html", + "dom", + "async", + "javascript" + ], + "require": { + "php": ">=7.1", + "ext-json": "*" + }, + "license": "MIT", + "authors": [ + { + "name": "Richard Dobroň" + } + ], + "minimum-stability": "dev", + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "dobron\\BigPipe\\": "src" + } + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=v3.0.0", + "phpunit/phpunit": ">=7.5.0" + }, + "scripts": { + "post-merge": "composer install", + "check-style": "php-cs-fixer fix --using-cache=no --diff --dry-run --ansi", + "fix-style": "php-cs-fixer fix --using-cache=no --ansi", + "test": "vendor/bin/phpunit --colors=always" + }, + "scripts-descriptions": { + "check-style": "Run style checks (only dry run - no fixing!).", + "fix-style": "Run style checks and fix violations.", + "test": "Run all tests." + } +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..a3a986a --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,14 @@ + + + The coding standard of bigpipe package + + + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9ae09d3 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + src/ + + + + + ./tests/ + + + diff --git a/src/AsyncResponse.php b/src/AsyncResponse.php new file mode 100644 index 0000000..1722580 --- /dev/null +++ b/src/AsyncResponse.php @@ -0,0 +1,291 @@ +bigPipe = new BigPipe(); + $this->transport = new TransportMarker(); + } + + /** + * Define DOM operation + * + * @param string $selector + * @param string|null $html + * @param string $method + * + * @return self + */ + private function defineDomOp(string $selector, ?string $html, string $method): self + { + $transport = null; + + if ($method === self::DOM_EVAL) { + $transport = $html; + } elseif (!in_array($method, [self::DOM_HIDE, self::DOM_SHOW, self::DOM_REMOVE], true)) { + $transport = $this->transport->transportHtml($html); + } + + $this->domops[] = [ + $method, + $selector, + !$selector, + $transport + ]; + + return $this; + } + + public function bigPipe(): BigPipe + { + return $this->bigPipe; + } + + public function transport(): TransportMarker + { + return $this->transport; + } + + /** + * Set payload + * + * @param mixed $data + * @return self + */ + public function setPayload($data): self + { + $this->payload = $data; + + return $this; + } + + /** + * Define eval script to evaluate + * + * @param string $context + * @param string $code + * + * @return self + */ + public function eval(string $context, string $code): self + { + return $this->defineDomOp($context, $code, self::DOM_EVAL); + } + + /** + * Define hide DOM operation + * + * @param string $selector + * + * @return self + */ + public function hide(string $selector): self + { + return $this->defineDomOp($selector, null, self::DOM_HIDE); + } + + /** + * Define show DOM operation + * + * @param string $selector + * + * @return self + */ + public function show(string $selector): self + { + return $this->defineDomOp($selector, null, self::DOM_SHOW); + } + + /** + * Define set content DOM operation + * + * @param string $selector + * @param string|null $html + * + * @return self + */ + public function setContent(string $selector, ?string $html): self + { + return $this->defineDomOp($selector, $html, self::DOM_SET_CONTENT); + } + + /** + * Define append content DOM operation + * + * @param string $selector + * @param string|null $html + * + * @return self + */ + public function appendContent(string $selector, ?string $html): self + { + return $this->defineDomOp($selector, $html, self::DOM_APPEND_CONTENT); + } + + /** + * Define prepend content DOM operation + * + * @param string $selector + * @param string|null $html + * + * @return self + */ + public function prependContent(string $selector, ?string $html): self + { + return $this->defineDomOp($selector, $html, self::DOM_PREPEND_CONTENT); + } + + /** + * Define insert after DOM operation + * + * @param string $selector + * @param string|null $html + * + * @return self + */ + public function insertAfter(string $selector, ?string $html): self + { + return $this->defineDomOp($selector, $html, self::DOM_INSERT_AFTER); + } + + /** + * Define insert before DOM operation + * + * @param string $selector + * @param string|null $html + * + * @return self + */ + public function insertBefore(string $selector, ?string $html): self + { + return $this->defineDomOp($selector, $html, self::DOM_INSERT_BEFORE); + } + + /** + * Define replace DOM operation + * + * @param string $selector + * @param string|null $html + * + * @return self + */ + public function replace(string $selector, ?string $html): self + { + return $this->defineDomOp($selector, $html, self::DOM_REPLACE); + } + + /** + * Define remove DOM operation + * + * @param string $selector + * + * @return self + */ + public function remove(string $selector): self + { + return $this->defineDomOp($selector, null, self::DOM_REMOVE); + } + + /** + * Reload a page. + * + * @param int $delay + * + * @return self + * @throws Exceptions\BigPipeInvalidArgumentException + */ + public function reload(int $delay = 0): self + { + if ($delay > 0) { + $this->bigPipe()->require("require('bigpipe-util/src/core/ReloadPage').delay()", [ + $delay, + ]); + } else { + $this->bigPipe()->require("require('bigpipe-util/src/core/ReloadPage').now()"); + } + + return $this; + } + + /** + * Redirect to the given url. + * + * @param string $url + * @param int $delay + * + * @return self + * @throws Exceptions\BigPipeInvalidArgumentException + */ + public function redirect(string $url, int $delay = 0): self + { + $this->bigPipe()->require("require('bigpipe-util/src/core/ServerRedirect').redirectPageTo()", [ + $url, + $delay, + ]); + + return $this; + } + + /** + * Get response + * + * @return array + */ + public function getResponse(): array + { + return [ + "payload" => $this->payload, + "domops" => $this->domops, + "jsmods" => BigPipe::jsmods(), + "__ar" => 1, + ]; + } + + /** + * Send response + * + * @return void + */ + public function send() + { + header("content-type: text/javascript"); + + echo json_encode($this->getResponse()); + + exit(); + } +} diff --git a/src/BigPipe.php b/src/BigPipe.php new file mode 100644 index 0000000..571e17a --- /dev/null +++ b/src/BigPipe.php @@ -0,0 +1,81 @@ +.+?)['\"\]]+\)(\.(?\w+)\(\))?$/"; + + private static $jsmods = [ + "require" => [], + ]; + + /** + * Check if require call is valid + * + * @param string $fragment + * + * @return bool + */ + private static function isValidRequireCall(string $fragment): bool + { + return !!preg_match(self::JAVASCRIPT_REQUIRE_REGEX, $fragment); + } + + /** + * Parse JavaScript fragment + * + * @param string $fragment + * @param bool $multiple + * + * @return array|null + */ + public static function parseRequireCall(string $fragment, bool $multiple = false): ?array + { + preg_match(self::JAVASCRIPT_REQUIRE_REGEX, $fragment, $match); + + if ($multiple) { + return preg_split("/',\s+'/", $match['module']); + } + + return [ + "module" => $match['module'] ?? null, + "method" => $match['method'] ?? null, + ]; + } + + /** + * @param string $fragment + * @param array $args + * @return $this + * @throws BigPipeInvalidArgumentException + */ + public function require(string $fragment, array $args = []): self + { + if (!self::isValidRequireCall($fragment)) { + throw new BigPipeInvalidArgumentException("Invalid call."); + } + + $fragmentParts = self::parseRequireCall($fragment); + + $require = [ + $fragmentParts['module'], + $fragmentParts['method'] ?? null, + ]; + + if (!empty($args)) { + $require[] = $args; + } + + static::$jsmods[__FUNCTION__][] = array_trim($require); + + return $this; + } + + public static function jsmods(): array + { + return static::$jsmods; + } +} diff --git a/src/Exceptions/BigPipeInvalidArgumentException.php b/src/Exceptions/BigPipeInvalidArgumentException.php new file mode 100644 index 0000000..a28ce31 --- /dev/null +++ b/src/Exceptions/BigPipeInvalidArgumentException.php @@ -0,0 +1,7 @@ + $data + ]; + } + + /** + * Create HTML transport marker + * + * @param null|string $content + * + * @return array + */ + public static function transportHtml(?string $content): array + { + return self::createTransportMarker($content, self::TRANSPORT_HTML); + } + + /** + * Create element transport marker + * + * @param string $elementId + * + * @return array + */ + public static function transportElement(string $elementId): array + { + return self::createTransportMarker($elementId, self::TRANSPORT_ELEMENT); + } + + /** + * Create Map object transport marker + * + * @param array $data + * + * @return array + */ + public static function transportMap(array $data): array + { + return self::createTransportMarker($data, self::TRANSPORT_MAP); + } + + /** + * Create Set objects transport marker + * + * @param array $data + * + * @return array + */ + public static function transportSet(array $data): array + { + return self::createTransportMarker($data, self::TRANSPORT_SET); + } +} diff --git a/src/helpers.php b/src/helpers.php new file mode 100644 index 0000000..ef2e249 --- /dev/null +++ b/src/helpers.php @@ -0,0 +1,12 @@ +bigPipe()->require( + "require('someFunction')", + [ + 'abc' + ] + ); + + $response->bigPipe()->require( + "require('Composer').init()", + [ + 123 + ] + ); + + $this->assertEquals($response->getResponse(), [ + 'payload' => [], + 'domops' => [], + 'jsmods' => [ + 'require' => [ + [ + 'someFunction', + null, + [ + 'abc' + ] + ], + [ + 'Composer', + 'init', + [ + 123 + ] + ] + ] + ], + '__ar' => 1, + ]); + } + + public function testOmittingEmptyMethodName(): void + { + $response = new AsyncResponse(); + + $response->bigPipe()->require("require('someFunction')"); + + $this->assertEquals($response->getResponse(), [ + 'payload' => [], + 'domops' => [], + 'jsmods' => [ + 'require' => [ + [ + 'someFunction', + ], + ] + ], + '__ar' => 1, + ]); + } + + public function testAsyncResponse(): void + { + $response = new AsyncResponse(); + + $response->setContent('#element', '

paragraph

'); + + $this->assertEquals($response->getResponse(), [ + 'payload' => [], + 'domops' => [ + [ + 'setContent', + '#element', + false, + [ + '__html' => '

paragraph

', + ] + ] + ], + 'jsmods' => [ + 'require' => [], + ], + '__ar' => 1, + ]); + } + + public function testReplaceWithoutSelector(): void + { + $response = new AsyncResponse(); + + $response->replace('', '

self replace

'); + + $this->assertEquals($response->getResponse(), [ + 'payload' => [], + 'domops' => [ + [ + 'replace', + '', + true, + [ + '__html' => '

self replace

', + ], + ] + ], + 'jsmods' => [ + 'require' => [], + ], + '__ar' => 1, + ]); + } + + public function testException(): void + { + $this->expectException(BigPipeInvalidArgumentException::class); + + $response = new AsyncResponse(); + + $response->bigPipe()->require("InvalidUsage.method()"); + } +} diff --git a/tests/TransportMarkersTest.php b/tests/TransportMarkersTest.php new file mode 100644 index 0000000..31572b3 --- /dev/null +++ b/tests/TransportMarkersTest.php @@ -0,0 +1,53 @@ +bigPipe()->require("require('Chart').setup()", [ + 'element' => TransportMarker::transportElement('chart-div'), + 'dataPoints' => $response->transport()->transportSet([ + ['x' => 10, 'y' => 71], + ['x' => 20, 'y' => 55], + ['x' => 30, 'y' => 50], + ['x' => 40, 'y' => 65], + ]), + ]); + + $this->assertEquals($response->getResponse(), [ + 'payload' => [], + 'domops' => [], + 'jsmods' => [ + 'require' => [ + [ + 'Chart', + 'setup', + [ + 'element' => ['__e' => 'chart-div'], + 'dataPoints' => ['__set' => [ + ['x' => 10, 'y' => 71], + ['x' => 20, 'y' => 55], + ['x' => 30, 'y' => 50], + ['x' => 40, 'y' => 65], + ]], + ] + ] + ], + ], + '__ar' => 1, + ]); + } +}