├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── LICENSE.md
├── README.md
├── codeception.yml
├── composer.json
├── docs
├── 01-Installation.md
└── README.md
├── src
├── Module.php
├── WpnAsset.php
├── assets
│ ├── sw.js
│ ├── tsc
│ ├── web-push.js
│ └── web-push.ts
├── commands
│ ├── AppController.php
│ └── KeysController.php
├── components
│ └── Pusher.php
├── controllers
│ └── DefaultController.php
├── exceptions
│ ├── InvalidApplication.php
│ └── SubscriptionNotFound.php
├── migrations
│ └── m210828_202754_create_wpn_table.php
├── models
│ ├── WpnApp.php
│ ├── WpnCampaign.php
│ ├── WpnReport.php
│ └── WpnSubscription.php
└── widgets
│ ├── SubscriptionWidget.php
│ └── views
│ └── subscription.php
├── tests
├── PusherTest.php
├── WebPushMock.php
├── _bootstrap.php
├── _output
│ └── .gitignore
├── _support
│ ├── .gitkeep
│ ├── Helper
│ │ └── Unit.php
│ ├── UnitTester.php
│ └── _generated
│ │ └── UnitTesterActions.php
├── config.php
└── fixtures
│ ├── WpnAppFixture.php
│ ├── WpnCampaignFixture.php
│ ├── WpnSubscriptionFixture.php
│ └── data
│ ├── wpn_app.php
│ ├── wpn_campaign.php
│ └── wpn_subscription.php
├── tsconfig.json
└── yii_test
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on: [push]
4 |
5 | jobs:
6 | tests:
7 | name: PHP ${{ matrix.php }} on ${{ matrix.os }}
8 | runs-on: ${{ matrix.os }}
9 |
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | os: [ubuntu-latest]
14 | php: ['7.3', '7.4', '8.0']
15 |
16 | services:
17 | mysql:
18 | image: mysql:5.7
19 | env:
20 | MYSQL_ROOT_PASSWORD: password
21 | MYSQL_DATABASE: wpn_test_db
22 | ports:
23 | - 3306:3306
24 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
25 |
26 | steps:
27 | - name: Checkout
28 | uses: actions/checkout@v2
29 |
30 | - name: Install PHP
31 | uses: shivammathur/setup-php@v2
32 | with:
33 | php-version: ${{ matrix.php }}
34 | tools: pecl
35 | extensions: curl
36 |
37 | - name: Get composer cache directory
38 | id: composer-cache
39 | run: echo "::set-output name=dir::$(composer config cache-files-dir)"
40 |
41 | - name: Cache composer dependencies
42 | uses: actions/cache@v1
43 | with:
44 | path: ${{ steps.composer-cache.outputs.dir }}
45 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
46 | restore-keys: ${{ runner.os }}-composer-
47 |
48 | - name: Install dependencies
49 | run: composer update $DEFAULT_COMPOSER_FLAGS
50 |
51 | - name: DB migrations
52 | run: ./yii_test migrate --migrationPath=src/migrations/ --interactive=0
53 |
54 | - name: Run test cases
55 | run: php vendor/bin/codecept run
56 |
57 |
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | vendor
3 | composer.lock
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2021 by Clockwork Consulting (https://clockwork.tn)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software LLC nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Web Push Notifications for Yii 2
6 |
7 |
8 |
9 | An extension for implementing Web Push Notifications on your website in a breeze.
10 |
11 | Documentation is at [docs/README.md](docs/README.md)
12 |
13 | [](https://packagist.org/packages/machour/yii2-web-push-notifications)
14 | [](https://packagist.org/packages/machour/yii2-web-push-notifications)
15 | [](https://github.com/machour/yii2-web-push-notifications/actions?query=workflow%3Atests)
16 |
17 |
18 | Installation
19 | ------------
20 |
21 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
22 |
23 | ```
24 | php composer.phar require --prefer-dist machour/yii2-web-push-notification
25 | ```
26 |
27 | Configuration
28 | -------------
29 |
30 | ### DB
31 |
32 |
33 | This module use the following tables:
34 |
35 | | Name | Role |
36 | |-------------------------|----------------------------------------------------------------------|
37 | | `{{%wpn_app}}` | Represents a Web Push application |
38 | | `{{%wpn_subscription}}` | Represents a Web Push subscriber |
39 | | `{{%wpn_campaign}}` | Represents a Web Push campaign (ie, a push you've scheduled or sent) |
40 | | `{{%wpn_report}}` | Links a subscriber to a push (received ? errored ? ..) |
41 |
42 | Use the following migration to create them:
43 | ```bash
44 | ./yii migrate --migrationPath=vendor/machour/yii2-web-push-notifications/src/migrations/
45 | ```
46 |
47 | ### Web
48 |
49 | Add this module to your `\yii\web\Application` config file :
50 |
51 | ```php
52 | return [
53 | // ...
54 | 'modules' => [
55 | 'wpn' => [
56 | 'class' => 'machour\yii2\wpn\Module',
57 | ],
58 | // ...
59 | ],
60 | ];
61 | ```
62 |
63 | ### Console
64 |
65 | Add this module to your `\yii\console\Application` config file :
66 |
67 | ```php
68 | return [
69 | // ...
70 | 'bootstrap' => [..., 'wpn'],
71 | // ...
72 | 'modules' => [
73 | 'wpn' => [
74 | 'class' => 'machour\yii2\wpn\Module',
75 | ],
76 | // ...
77 | ],
78 | ]
79 | ```
80 |
81 | You can now register a new application using the `./yii wpn/app/create` console command.
82 | Arguments are as follow:
83 |
84 | ```
85 | USAGE
86 |
87 | yii wpn/app/create [...options...]
88 |
89 | - name (required): string
90 | The application name
91 |
92 | - host (required): string
93 | The hostname where the application will be deployed
94 |
95 | - subject (required): string
96 | The contact for the application. Needs to be a URL or a mailto: URL.
97 | This provides a point of contact in case the push service needs to contact you
98 | ```
99 |
--------------------------------------------------------------------------------
/codeception.yml:
--------------------------------------------------------------------------------
1 | namespace: tests
2 | bootstrap: _bootstrap.php
3 | suites:
4 | unit:
5 | path: .
6 | actor: UnitTester
7 | modules:
8 | enabled:
9 | - Yii2:
10 | configFile: 'tests/config.php'
11 | part: [ orm, fixtures ]
12 | # add more modules here
13 | - Asserts
14 | step_decorators: ~
15 |
16 | settings:
17 | shuffle: true
18 | lint: true
19 | paths:
20 | tests: tests
21 | output: tests/_output
22 | support: tests/_support
23 | data: tests/fixtures/data
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "machour/yii2-web-push-notifications",
3 | "description": "Web Push Notifications for the Yii framework",
4 | "keywords": [
5 | "yii2",
6 | "push",
7 | "web push"
8 | ],
9 | "type": "yii2-extension",
10 | "license": "BSD-3-Clause",
11 | "authors": [
12 | {
13 | "name": "Mehdi Achour",
14 | "email": "machour@gmail.com"
15 | }
16 | ],
17 | "minimum-stability": "stable",
18 | "require": {
19 | "php": ">=7.3",
20 | "yiisoft/yii2": "~2.0.13",
21 | "minishlink/web-push": "^6.0",
22 | "matomo/device-detector": "^4.3"
23 | },
24 | "require-dev": {
25 | "codeception/codeception": "^4.1",
26 | "codeception/module-yii2": "^1.1",
27 | "codeception/module-phpbrowser": "^1.0.0",
28 | "codeception/module-asserts": "^1.0.0",
29 | "codeception/assert-throws": "^1.1"
30 | },
31 | "autoload": {
32 | "psr-4": {
33 | "machour\\yii2\\wpn\\": "src"
34 | }
35 | },
36 | "autoload-dev": {
37 | "psr-4": {
38 | "tests\\": "tests"
39 | }
40 | },
41 | "repositories": [
42 | {
43 | "type": "composer",
44 | "url": "https://asset-packagist.org"
45 | }
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/docs/01-Installation.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machour/yii2-web-push-notifications/f99a4a4e194131410c95057a916ea545967a1eb4/docs/01-Installation.md
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Web Push Notifications for Yii2
2 |
3 | This module provides a complete solution to implement your own Web Push notifications in your Yii2 application.
4 |
5 | You won't need to rely anymore on a service provider like TruePush, OneSignal, ..
6 |
7 | Out of the box, this module allows you to:
8 |
9 | * Quickly set up a Web Push subscription mechanism on your website
10 | * Migrate from your current Web Push provider and own your subscribers
11 |
12 |
13 | ## Summary (TODO)
14 |
15 | * Installation
16 | * Concepts
17 | * Basic Usage
18 | * Migrating from a Push Provider
19 |
20 | ## Credits
21 |
22 | This module is based on the [minishlink/web-push](https://github.com/web-push-libs/web-push-php) library.
23 |
24 | It began as a fork of the [web-push-php-example](https://github.com/Minishlink/web-push-php-example) repository and
25 | turned into a full solution.
26 |
27 | ## Resources
28 |
29 | Check out these links if you want to get a better understanding of the underlying mechanisms in action for Web Push
30 | Notifications :
31 |
32 | * Matthew Gaunt's [Web Push Book](https://web-push-book.gauntface.com/) - a masterpiece.
33 | * This [web-push-php-example](https://github.com/Minishlink/web-push-php-example) - a simple yet efficient demo repository
34 | that you can clone and and run locally to start your journey
35 | * Autopush's [Error codes](https://autopush.readthedocs.io/en/latest/http.html#error-codes) manual section, that documents
36 | all possible error codes when performing a push.
--------------------------------------------------------------------------------
/src/Module.php:
--------------------------------------------------------------------------------
1 | controllerNamespace = 'machour\yii2\wpn\commands';
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/WpnAsset.php:
--------------------------------------------------------------------------------
1 | {
2 | console.log("Broadcasting message", payload)
3 | fetch(`/web-push/report`, {
4 | method: 'POST',
5 | body: JSON.stringify(payload)
6 | }).then(() => {
7 | console.log("Action reported");
8 | }
9 | ).catch(error => {
10 | console.log("Action reporting failed", error);
11 | });
12 | }
13 |
14 | self.addEventListener("push", function (event) {
15 | console.log("in push event listener", event);
16 | if (!(self.Notification && self.Notification.permission === "granted")) {
17 | console.error("No notification ?");
18 | return;
19 | }
20 |
21 | const sendNotification = payload => {
22 | console.log("Got payload", payload);
23 | const decoded = JSON.parse(payload);
24 |
25 | const {title, ...options} = decoded;
26 |
27 | return self.registration.showNotification(title, options).then(() => {
28 | reportAction( {
29 | action: "view",
30 | endpoint: event.notification.data.endpoint,
31 | campaignId: event.notification.data.campaignId
32 | });
33 | });
34 | };
35 |
36 | if (event.data) {
37 | const message = event.data.text();
38 | event.waitUntil(sendNotification(message));
39 |
40 | }
41 | });
42 |
43 | self.addEventListener("notificationclick", function (event) {
44 | event.notification.close();
45 | // This looks to see if the current window is already open and focuses if it is
46 | event.waitUntil(
47 | clients.matchAll({
48 | type: "window"
49 | }).then(function () {
50 | if (clients.openWindow) {
51 | const data = event.notification.data; // the payload from before
52 | return clients.openWindow(data.url); // open it
53 | }
54 | }).then(() => {
55 | reportAction( {
56 | action: "click",
57 | endpoint: event.notification.data.endpoint,
58 | campaignId: event.notification.data.campaignId
59 | });
60 | })
61 | );
62 | });
63 |
64 | self.addEventListener("install", function (e) {
65 | e.waitUntil(self.skipWaiting());
66 | });
67 |
68 | self.addEventListener("notificationclose", function (e) {
69 | reportAction( {
70 | action: "dismiss",
71 | campaignId: e.notification.data.campaignId,
72 | endpoint: e.notification.data.endpoint,
73 | });
74 | });
75 |
76 | self.addEventListener("activate", function (e) {
77 | e.waitUntil(self.clients.claim());
78 | });
--------------------------------------------------------------------------------
/src/assets/tsc:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | tsc --lib es2015,dom web-push.ts
3 |
--------------------------------------------------------------------------------
/src/assets/web-push.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __assign = (this && this.__assign) || function () {
3 | __assign = Object.assign || function(t) {
4 | for (var s, i = 1, n = arguments.length; i < n; i++) {
5 | s = arguments[i];
6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 | t[p] = s[p];
8 | }
9 | return t;
10 | };
11 | return __assign.apply(this, arguments);
12 | };
13 | exports.__esModule = true;
14 | var SubscriptionStatus;
15 | (function (SubscriptionStatus) {
16 | SubscriptionStatus["SUBSCRIBED"] = "subscribed";
17 | SubscriptionStatus["UNSUBSCRIBED"] = "unsubscribed";
18 | SubscriptionStatus["UNKNOWN"] = "unknown";
19 | SubscriptionStatus["BLOCKED"] = "blocked";
20 | })(SubscriptionStatus || (SubscriptionStatus = {}));
21 | var WebPush = /** @class */ (function () {
22 | function WebPush(appId, publicKey, controller, syncInterval) {
23 | if (controller === void 0) { controller = "/wpn/default"; }
24 | if (syncInterval === void 0) { syncInterval = 1000 * 60 * 10; }
25 | this.LS = {
26 | ENDPOINT: "yii2-wpn-endpoint",
27 | LAST_SYNC: "yii2-wpn-last_sync"
28 | };
29 | this.appId = appId;
30 | this.publicKey = publicKey;
31 | this.controller = controller;
32 | this.syncInterval = syncInterval;
33 | }
34 | WebPush.prototype.setupRegistration = function (swPath, successCb, failureCb, shouldMigrate) {
35 | var _this = this;
36 | if (swPath === void 0) { swPath = "/sw.js"; }
37 | if (successCb === void 0) { successCb = function (status) { }; }
38 | if (failureCb === void 0) { failureCb = function (error) { }; }
39 | if (shouldMigrate === void 0) { shouldMigrate = function (context) { return false; }; }
40 | if (!("serviceWorker" in navigator)) {
41 | this.log("Service workers are not supported by this browser");
42 | return false;
43 | }
44 | else if (!("PushManager" in window)) {
45 | this.log("Push notifications are not supported by this browser");
46 | return false;
47 | }
48 | else if (!("showNotification" in ServiceWorkerRegistration.prototype)) {
49 | this.log("Notifications are not supported by this browser");
50 | return false;
51 | }
52 | else if (Notification.permission === "denied") {
53 | this.log("Notifications are denied by the user");
54 | return false;
55 | }
56 | navigator.serviceWorker.register(swPath).then(function () {
57 | var lastSync = parseInt(localStorage.getItem(_this.LS.LAST_SYNC));
58 | var now = Date.now();
59 | if (!lastSync || (now - lastSync > _this.syncInterval)) {
60 | console.log(now, lastSync, now - lastSync, _this.syncInterval);
61 | _this.checkSubscription(successCb, failureCb, shouldMigrate);
62 | localStorage.setItem(_this.LS.LAST_SYNC, String(now));
63 | }
64 | }, function (err) {
65 | _this.log("ServiceWorker registration failed: ", err);
66 | });
67 | };
68 | WebPush.prototype.checkSubscription = function (successCb, failureCb, shouldMigrate) {
69 | var _this = this;
70 | if (successCb === void 0) { successCb = function (status) { }; }
71 | if (failureCb === void 0) { failureCb = function (error) { }; }
72 | if (shouldMigrate === void 0) { shouldMigrate = function (context) { return false; }; }
73 | navigator.serviceWorker.ready
74 | .then(function (serviceWorkerRegistration) {
75 | return serviceWorkerRegistration.pushManager.getSubscription();
76 | })
77 | .then(function (subscription) {
78 | if (!subscription) {
79 | // We aren't subscribed to push, so set UI to allow the user to enable push
80 | successCb(SubscriptionStatus.UNSUBSCRIBED);
81 | return;
82 | }
83 | // We are subscribed, give the possibility to migrate from an old provider
84 | if (shouldMigrate(_this)) {
85 | return subscription
86 | .unsubscribe()
87 | .then(function () { return navigator.serviceWorker.ready; })
88 | .then(function (serviceWorkerRegistration) {
89 | return serviceWorkerRegistration.pushManager.subscribe({
90 | userVisibleOnly: true,
91 | applicationServerKey: _this.urlBase64ToUint8Array(_this.publicKey)
92 | });
93 | })
94 | .then(function (subscription) {
95 | return _this.sync(subscription, "POST");
96 | });
97 | }
98 | return _this.sync(subscription, "PUT");
99 | })
100 | .then(function (subscription) { return subscription && successCb(SubscriptionStatus.SUBSCRIBED); }) // Set your UI to show they have subscribed for push messages
101 | ["catch"](function (e) {
102 | _this.log("Error when updating the subscription", e);
103 | failureCb(e);
104 | });
105 | };
106 | WebPush.prototype.subscribe = function (success, failure) {
107 | var _this = this;
108 | if (success === void 0) { success = function (s) { }; }
109 | if (failure === void 0) { failure = function (error) { }; }
110 | return this.checkNotificationPermission()
111 | .then(function () { return navigator.serviceWorker.ready; })
112 | .then(function (serviceWorkerRegistration) {
113 | return serviceWorkerRegistration.pushManager.subscribe({
114 | userVisibleOnly: true,
115 | applicationServerKey: _this.urlBase64ToUint8Array(_this.publicKey)
116 | });
117 | })
118 | .then(function (subscription) {
119 | // Subscription was successful
120 | // create subscription on your server
121 | localStorage.setItem(_this.LS.ENDPOINT, subscription.endpoint);
122 | return _this.sync(subscription, "POST");
123 | })
124 | .then(function (subscription) { return success(subscription); }) // update your UI
125 | ["catch"](function (e) {
126 | if (Notification.permission === "denied") {
127 | // The user denied the notification permission which
128 | // means we failed to subscribe and the user will need
129 | // to manually change the notification permission to
130 | // subscribe to push messages
131 | failure("Notifications are denied by the user.");
132 | }
133 | else {
134 | // A problem occurred with the subscription; common reasons
135 | // include network errors or the user skipped the permission
136 | failure("Impossible to subscribe to push notifications : " + e);
137 | }
138 | });
139 | };
140 | WebPush.prototype.unsubscribe = function (success, failure) {
141 | var _this = this;
142 | if (success === void 0) { success = function () { }; }
143 | if (failure === void 0) { failure = function () { }; }
144 | // To unsubscribe from push messaging, you need to get the subscription object
145 | navigator.serviceWorker.ready
146 | .then(function (serviceWorkerRegistration) {
147 | return serviceWorkerRegistration.pushManager.getSubscription();
148 | })
149 | .then(function (subscription) {
150 | // Check that we have a subscription to unsubscribe
151 | if (!subscription) {
152 | // No subscription object, so set the state
153 | // to allow the user to subscribe to push
154 | success();
155 | return;
156 | }
157 | // We have a subscription, unsubscribe
158 | // Remove push subscription from server
159 | return _this.sync(subscription, "DELETE");
160 | })
161 | .then(function (subscription) { return subscription.unsubscribe(); })
162 | .then(function () {
163 | localStorage.removeItem(_this.LS.ENDPOINT);
164 | success();
165 | })["catch"](function (e) {
166 | // We failed to unsubscribe, this can lead to
167 | // an unusual state, so it may be best to remove
168 | // the users data from your data store and
169 | // inform the user that you have done so
170 | _this.log("Error when unsubscribing the user", e);
171 | failure();
172 | });
173 | };
174 | WebPush.prototype.sync = function (subscription, method) {
175 | var _this = this;
176 | var contentEncoding = (PushManager.supportedContentEncodings || [
177 | "aesgcm"
178 | ])[0];
179 | localStorage.setItem(this.LS.ENDPOINT, subscription.endpoint);
180 | return new Promise(function (resolve, reject) {
181 | $.ajax({
182 | url: _this.controller + "/sync?appId=" + _this.appId,
183 | type: method,
184 | data: JSON.stringify(__assign({ endpoint: subscription.endpoint, publicKey: _this.encode(subscription.getKey("p256dh")), authToken: _this.encode(subscription.getKey("auth")), contentEncoding: contentEncoding }, _this.getCsrfParams())),
185 | success: function (result) {
186 | resolve(subscription);
187 | },
188 | error: function (err) {
189 | _this.log("WPN Sync error: ", err);
190 | reject(err);
191 | }
192 | });
193 | });
194 | };
195 | WebPush.prototype.checkNotificationPermission = function () {
196 | return new Promise(function (resolve, reject) {
197 | if (Notification.permission === "denied") {
198 | return reject(new Error("Push messages are blocked."));
199 | }
200 | if (Notification.permission === "granted") {
201 | return resolve();
202 | }
203 | if (Notification.permission === "default") {
204 | return Notification.requestPermission().then(function (result) {
205 | if (result !== "granted") {
206 | reject(new Error("Bad permission result"));
207 | }
208 | else {
209 | resolve();
210 | }
211 | });
212 | }
213 | return reject(new Error("Unknown permission"));
214 | });
215 | };
216 | WebPush.prototype.urlBase64ToUint8Array = function (base64String) {
217 | var padding = "=".repeat((4 - (base64String.length % 4)) % 4);
218 | var base64 = (base64String + padding)
219 | .replace(/\-/g, "+")
220 | .replace(/_/g, "/");
221 | var rawData = window.atob(base64);
222 | var outputArray = new Uint8Array(rawData.length);
223 | for (var i = 0; i < rawData.length; ++i) {
224 | outputArray[i] = rawData.charCodeAt(i);
225 | }
226 | return outputArray;
227 | };
228 | WebPush.prototype.getCsrfParams = function () {
229 | var _a;
230 | return _a = {},
231 | _a[jQuery("meta[name=csrf-param]").attr("content")] = jQuery("meta[name=csrf-token]").attr("content"),
232 | _a;
233 | };
234 | WebPush.prototype.encode = function (str) {
235 | if (!str) {
236 | return null;
237 | }
238 | return btoa(String.fromCharCode.apply(null, new Uint8Array(str)));
239 | };
240 | WebPush.prototype.log = function () {
241 | var params = [];
242 | for (var _i = 0; _i < arguments.length; _i++) {
243 | params[_i] = arguments[_i];
244 | }
245 | console.log.apply(console, params);
246 | };
247 | return WebPush;
248 | }());
249 | exports.WebPush = WebPush;
250 |
--------------------------------------------------------------------------------
/src/assets/web-push.ts:
--------------------------------------------------------------------------------
1 | enum SubscriptionStatus {
2 | SUBSCRIBED = 'subscribed',
3 | UNSUBSCRIBED = 'unsubscribed',
4 | UNKNOWN = 'unknown',
5 | BLOCKED = 'blocked',
6 | }
7 |
8 | export class WebPush {
9 | readonly appId;
10 | readonly publicKey;
11 | readonly controller;
12 |
13 | private readonly LS = {
14 | ENDPOINT: "yii2-wpn-endpoint",
15 | LAST_SYNC: "yii2-wpn-last_sync",
16 | };
17 | private readonly syncInterval: number;
18 |
19 | constructor(
20 | appId: number,
21 | publicKey: string,
22 | controller = "/wpn/default",
23 | syncInterval = 1000 * 60 * 10
24 | ) {
25 | this.appId = appId;
26 | this.publicKey = publicKey;
27 | this.controller = controller;
28 | this.syncInterval = syncInterval;
29 | }
30 |
31 | setupRegistration(swPath = "/sw.js", successCb = (status: SubscriptionStatus) => {}, failureCb = (error) => {}, shouldMigrate = (context) => false) {
32 | if (!("serviceWorker" in navigator)) {
33 | this.log("Service workers are not supported by this browser");
34 | return false;
35 | } else if (!("PushManager" in window)) {
36 | this.log("Push notifications are not supported by this browser");
37 | return false;
38 | } else if (!("showNotification" in ServiceWorkerRegistration.prototype)) {
39 | this.log("Notifications are not supported by this browser");
40 | return false;
41 | } else if (Notification.permission === "denied") {
42 | this.log("Notifications are denied by the user");
43 | return false;
44 | }
45 |
46 | navigator.serviceWorker.register(swPath).then(
47 | () => {
48 | const lastSync = parseInt(localStorage.getItem(this.LS.LAST_SYNC));
49 | const now = Date.now();
50 |
51 | if (!lastSync || (now - lastSync > this.syncInterval)) {
52 | console.log(now, lastSync, now - lastSync, this.syncInterval)
53 | this.checkSubscription(successCb, failureCb, shouldMigrate);
54 | localStorage.setItem(this.LS.LAST_SYNC, String(now));
55 | }
56 | },
57 | (err) => {
58 | this.log("ServiceWorker registration failed: ", err);
59 | }
60 | );
61 | }
62 |
63 | checkSubscription(successCb = (status: SubscriptionStatus) => {}, failureCb = (error) => {}, shouldMigrate = (context) => false) {
64 | navigator.serviceWorker.ready
65 | .then(serviceWorkerRegistration =>
66 | serviceWorkerRegistration.pushManager.getSubscription()
67 | )
68 | .then(subscription => {
69 | if (!subscription) {
70 | // We aren't subscribed to push, so set UI to allow the user to enable push
71 | successCb(SubscriptionStatus.UNSUBSCRIBED);
72 | return;
73 | }
74 |
75 | // We are subscribed, give the possibility to migrate from an old provider
76 | if (shouldMigrate(this)) {
77 | return subscription
78 | .unsubscribe()
79 | .then(() => navigator.serviceWorker.ready)
80 | .then(serviceWorkerRegistration =>
81 | serviceWorkerRegistration.pushManager.subscribe({
82 | userVisibleOnly: true,
83 | applicationServerKey: this.urlBase64ToUint8Array(this.publicKey)
84 | })
85 | )
86 | .then(subscription => {
87 | return this.sync(subscription, "POST");
88 | });
89 | }
90 |
91 | return this.sync(subscription, "PUT");
92 | })
93 | .then(
94 | subscription => subscription && successCb(SubscriptionStatus.SUBSCRIBED)
95 | ) // Set your UI to show they have subscribed for push messages
96 | .catch(e => {
97 | this.log("Error when updating the subscription", e);
98 | failureCb(e);
99 | });
100 | }
101 |
102 | subscribe(success = (s: PushSubscription) => {}, failure = (error) => {}) {
103 | return this.checkNotificationPermission()
104 | .then(() => navigator.serviceWorker.ready)
105 | .then(serviceWorkerRegistration =>
106 | serviceWorkerRegistration.pushManager.subscribe({
107 | userVisibleOnly: true,
108 | applicationServerKey: this.urlBase64ToUint8Array(this.publicKey)
109 | })
110 | )
111 | .then((subscription: PushSubscription) => {
112 | // Subscription was successful
113 | // create subscription on your server
114 | localStorage.setItem(this.LS.ENDPOINT, subscription.endpoint);
115 | return this.sync(subscription, "POST");
116 | })
117 | .then(
118 | subscription => success(subscription)
119 | ) // update your UI
120 | .catch(e => {
121 | if (Notification.permission === "denied") {
122 | // The user denied the notification permission which
123 | // means we failed to subscribe and the user will need
124 | // to manually change the notification permission to
125 | // subscribe to push messages
126 | failure("Notifications are denied by the user.");
127 | } else {
128 | // A problem occurred with the subscription; common reasons
129 | // include network errors or the user skipped the permission
130 | failure("Impossible to subscribe to push notifications : " + e);
131 | }
132 | });
133 | }
134 |
135 | unsubscribe(success = () => {}, failure = () => {}) {
136 | // To unsubscribe from push messaging, you need to get the subscription object
137 | navigator.serviceWorker.ready
138 | .then(serviceWorkerRegistration =>
139 | serviceWorkerRegistration.pushManager.getSubscription()
140 | )
141 | .then(subscription => {
142 | // Check that we have a subscription to unsubscribe
143 | if (!subscription) {
144 | // No subscription object, so set the state
145 | // to allow the user to subscribe to push
146 | success();
147 | return;
148 | }
149 |
150 | // We have a subscription, unsubscribe
151 | // Remove push subscription from server
152 | return this.sync(subscription, "DELETE");
153 | })
154 | .then(subscription => subscription.unsubscribe())
155 | .then(() => {
156 | localStorage.removeItem(this.LS.ENDPOINT);
157 | success();
158 | })
159 | .catch(e => {
160 | // We failed to unsubscribe, this can lead to
161 | // an unusual state, so it may be best to remove
162 | // the users data from your data store and
163 | // inform the user that you have done so
164 | this.log("Error when unsubscribing the user", e);
165 | failure();
166 | });
167 | }
168 |
169 | sync(subscription, method): Promise {
170 | const contentEncoding = (PushManager.supportedContentEncodings || [
171 | "aesgcm"
172 | ])[0];
173 |
174 | localStorage.setItem(this.LS.ENDPOINT, subscription.endpoint);
175 | return new Promise((resolve, reject) => {
176 | $.ajax({
177 | url: `${this.controller}/sync?appId=${this.appId}`,
178 | type: method,
179 | data: JSON.stringify({
180 | endpoint: subscription.endpoint,
181 | publicKey: this.encode(subscription.getKey("p256dh")),
182 | authToken: this.encode(subscription.getKey("auth")),
183 | contentEncoding,
184 | ...this.getCsrfParams()
185 | }),
186 | success: result => {
187 | resolve(subscription);
188 | },
189 | error: err => {
190 | this.log("WPN Sync error: ", err);
191 | reject(err);
192 | }
193 | });
194 | });
195 | }
196 |
197 | checkNotificationPermission() {
198 | return new Promise((resolve, reject) => {
199 | if (Notification.permission === "denied") {
200 | return reject(new Error("Push messages are blocked."));
201 | }
202 |
203 | if (Notification.permission === "granted") {
204 | return resolve();
205 | }
206 |
207 | if (Notification.permission === "default") {
208 | return Notification.requestPermission().then(result => {
209 | if (result !== "granted") {
210 | reject(new Error("Bad permission result"));
211 | } else {
212 | resolve();
213 | }
214 | });
215 | }
216 |
217 | return reject(new Error("Unknown permission"));
218 | });
219 | }
220 |
221 | urlBase64ToUint8Array(base64String) {
222 | const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
223 | const base64 = (base64String + padding)
224 | .replace(/\-/g, "+")
225 | .replace(/_/g, "/");
226 |
227 | const rawData = window.atob(base64);
228 | const outputArray = new Uint8Array(rawData.length);
229 |
230 | for (let i = 0; i < rawData.length; ++i) {
231 | outputArray[i] = rawData.charCodeAt(i);
232 | }
233 | return outputArray;
234 | }
235 |
236 | getCsrfParams() {
237 | return {
238 | [jQuery("meta[name=csrf-param]").attr("content")]: jQuery(
239 | "meta[name=csrf-token]"
240 | ).attr("content")
241 | };
242 | }
243 |
244 | encode(str) {
245 | if (!str) {
246 | return null;
247 | }
248 |
249 | return btoa(String.fromCharCode.apply(null, new Uint8Array(str)));
250 | }
251 |
252 | log(...params) {
253 | console.log(...params);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/commands/AppController.php:
--------------------------------------------------------------------------------
1 | all();
16 |
17 | $rows = [];
18 |
19 | foreach ($apps as $app) {
20 | $rows[] = [$app->id, $app->name, $app->host, $app->subject, $app->enabled ? $this->ansiFormat('✓', Console::FG_GREEN) : $this->ansiFormat('x', Console::FG_RED)];
21 | }
22 |
23 | echo Table::widget([
24 | 'headers' => ['ID', 'Name', 'Host', 'Subject', 'Enabled'],
25 | 'rows' => $rows
26 | ]);
27 | }
28 |
29 | /**
30 | * @param string $name The application name
31 | * @param string $host The hostname where the application will be deployed
32 | * @param string $subject The contact for the application. Needs to be a URL or a mailto: URL.
33 | * This provides a point of contact in case the push service needs to contact you
34 | */
35 | public function actionCreate($name, $host, $subject)
36 | {
37 | try {
38 | $keys = VAPID::createVapidKeys();
39 | $app = new WpnApp([
40 | 'name' => $name,
41 | 'host' => $host,
42 | 'subject' => $subject,
43 | 'private_key' => $keys['privateKey'],
44 | 'public_key' => $keys['publicKey'],
45 | 'enabled' => true,
46 | ]);
47 |
48 | if ($app->save()) {
49 | $this->stdout("Application #{$app->id} created and enabled.\n", Console::FG_GREEN);
50 | } else {
51 | $this->stdout("Application could not be created. Please fix the following errors:\n\n", Console::FG_RED);
52 | foreach ($app->errors as $attribute => $errors) {
53 | echo ' - ' . $this->ansiFormat($attribute, Console::FG_CYAN) . ': ' . implode('; ', $errors) . "\n";
54 | }
55 | }
56 | } catch (\ErrorException $e) {
57 | $this->stdout("Could not generate VapId keys: {$e->getMessage()}\n", Console::FG_RED);
58 | }
59 |
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/src/commands/KeysController.php:
--------------------------------------------------------------------------------
1 | wp = $wp;
24 | parent::__construct($config);
25 | }
26 |
27 | /**
28 | * @throws \yii\db\Exception
29 | * @throws \ErrorException
30 | * @throws InvalidApplication
31 | */
32 | public function sendPush(WpnCampaign $campaign)
33 | {
34 | if (!$campaign->app->enabled) {
35 | throw new InvalidApplication();
36 | }
37 |
38 | $auth = [
39 | 'VAPID' => [
40 | 'subject' => $campaign->app->subject,
41 | 'publicKey' => $campaign->app->public_key,
42 | 'privateKey' => $campaign->app->private_key,
43 | ],
44 | ];
45 |
46 |
47 |
48 | $query = WpnSubscription::find()
49 | ->where(['subscribed' => true, 'app_id' => $campaign->app_id])
50 | ->orderBy(['last_seen' => SORT_DESC]);
51 |
52 | if ($campaign->test_only) {
53 | $query->andWhere(['test_user' => 1]);
54 | }
55 |
56 | /** @var WpnSubscription[] $subscriptions */
57 | $subscriptions = ArrayHelper::index($query->all(), 'endpoint');
58 |
59 | foreach ($subscriptions as $subscription) {
60 | try {
61 | $options = $campaign->options;
62 | $options['data']['endpoint'] = $subscription->endpoint;
63 |
64 | $this->wp->queueNotification($subscription, Json::encode($options), [], $auth);
65 | } catch (\Exception $e) {
66 | $subscription->last_error = $e->getMessage();
67 | $subscription->save();
68 | }
69 | }
70 |
71 | $unsubscribedIds = [];
72 |
73 | $reports = [];
74 |
75 | foreach ($this->wp->flush() as $report) {
76 | $endpoint = $report->getRequest()->getUri()->__toString();
77 | $subscription = $subscriptions[$endpoint];
78 | $spParams = [
79 | 'subscription_id' => $subscription->id,
80 | 'campaign_id' => $campaign->id,
81 | 'sent_at' => date('Y-m-d H:i:s'),
82 | 'received' => true,
83 | ];
84 |
85 | if (!$report->isSuccess()) {
86 | $spParams['received'] = false;
87 | $response = $report->getResponse();
88 | if ($report->isSubscriptionExpired()) {
89 | $unsubscribedIds[] = $subscription->id;
90 | } else if ($response->getStatusCode() === 301) {
91 | $newEndpoint = $response->getHeaderLine('Location');
92 | if ($newEndpoint) {
93 | $subscription->endpoint = $newEndpoint;
94 | $subscription->save();
95 | } else {
96 | $unsubscribedIds[] = $subscription->id;
97 | }
98 | }
99 | }
100 |
101 | $reports[] = $spParams;
102 |
103 | }
104 |
105 | if (count($unsubscribedIds)) {
106 | WpnSubscription::updateAll([
107 | 'subscribed' => false,
108 | 'reason' => 'last push failed'
109 | ], ['id' => $unsubscribedIds]);
110 | }
111 |
112 | if (count($reports)) {
113 | Yii::$app->db->createCommand()
114 | ->batchInsert(
115 | '{{%wpn_report}}', array_keys($reports[0]), $reports)
116 | ->execute();
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/src/controllers/DefaultController.php:
--------------------------------------------------------------------------------
1 | id == 'report') {
29 | $this->enableCsrfValidation = false;
30 | }
31 |
32 | return parent::beforeAction($action);
33 | }
34 |
35 | public function behaviors(): array
36 | {
37 | return [
38 | 'verbs' => [
39 | 'class' => VerbFilter::class,
40 | 'actions' => [
41 | 'sync' => ['post', 'put', 'delete'],
42 | 'report' => ['post'],
43 | 'push' => ['get'],
44 | ],
45 | ],
46 | ];
47 | }
48 |
49 | /**
50 | * @throws SubscriptionNotFound
51 | * @throws Exception
52 | */
53 | public function actionSync($appId): \yii\web\Response
54 | {
55 | $request = Yii::$app->request;
56 | $data = Json::decode($request->rawBody);
57 |
58 | if (!isset($data['endpoint'])) {
59 | return $this->asJson([
60 | 'success' => false,
61 | 'message' => 'Not a subscription',
62 | ]);
63 | }
64 |
65 | switch ($request->method) {
66 | case 'POST':
67 |
68 | $subscription = new WpnSubscription([
69 | 'app_id' => $appId,
70 | 'endpoint' => $data['endpoint'],
71 | 'auth' => $data['authToken'],
72 | 'public_key' => $data['publicKey'],
73 | 'content_encoding' => $data['contentEncoding'],
74 | 'subscribed' => true,
75 | 'yii_user_id' => Yii::$app->user->id,
76 | 'ua' => $request->userAgent,
77 | 'ip' => $request->remoteIP,
78 | 'test_user' => false,
79 | 'last_seen' => new Expression('NOW()'),
80 | ]);
81 |
82 | $dd = new DeviceDetector($subscription->ua);
83 | $dd->parse();
84 | $subscription->os = $dd->getOs('name');
85 | $subscription->browser = $dd->getClient('name');
86 |
87 | if ($subscription->save()) {
88 | return $this->asJson(['success' => true, 'user_id' => $subscription->id]);
89 | } else {
90 | return $this->asJson(['success' => false, 'message' => var_export($subscription->errors, 1)]);
91 | }
92 |
93 | case 'PUT':
94 | $subscription = WpnSubscription::findOne(['endpoint' => $data['endpoint']]);
95 | if ($subscription) {
96 | $subscription->setAttributes([
97 | 'subscribed' => true,
98 | 'auth' => $data['authToken'],
99 | 'public_key' => $data['publicKey'],
100 | 'content_encoding' => $data['contentEncoding'],
101 | 'ua' => $request->userAgent,
102 | 'ip' => $request->remoteIP,
103 | 'last_seen' => new Expression('NOW()'),
104 | ]);
105 | if (!Yii::$app->user->isGuest) {
106 | $subscription->yii_user_id = Yii::$app->user->id;
107 | }
108 |
109 | if ($subscription->save()) {
110 | return $this->asJson(['success' => true, 'user_id' => $subscription->id]);
111 | }
112 |
113 | return $this->asJson(['success' => false, 'message' => var_export($subscription->errors, 1)]);
114 | }
115 |
116 | throw new SubscriptionNotFound();
117 |
118 | case 'DELETE':
119 | $subscription = WpnSubscription::findOne(['endpoint' => $data['endpoint'], 'subscribed' => true]);
120 | if ($subscription) {
121 | $subscription->subscribed = false;
122 | $subscription->reason = 'user request';
123 | $subscription->save();
124 | return $this->asJson(['success' => true, 'user_id' => $subscription->id]);
125 | }
126 |
127 | return $this->asJson(['success' => false, 'message' => 'Subscription not found']);
128 | }
129 |
130 | }
131 |
132 | public function actionReport(): \yii\web\Response
133 | {
134 | $request = Yii::$app->request;
135 | $data = Json::decode($request->rawBody);
136 |
137 | $campaign_id = $data['campaignId'];
138 | $endpoint = $data['endpoint'];
139 | $action = $data['action'];
140 |
141 | $campaign = WpnCampaign::findOne($campaign_id);
142 | if (!$campaign) {
143 | throw new InvalidArgumentException("Campaign not found");
144 | }
145 |
146 | $subscription = WpnSubscription::findOne(['endpoint' => $endpoint, 'app_id' => $campaign->app_id]);
147 | if (!$subscription) {
148 | throw new InvalidArgumentException("Subscription not found");
149 | }
150 |
151 | $sp = WpnReport::findOne(['campaign_id' => $campaign_id, 'subscription_id' => $subscription->id]);
152 | if (!$sp) {
153 | throw new InvalidArgumentException("Push not sent to this user");
154 | }
155 |
156 | switch ($action) {
157 | case 'dismiss':
158 | $sp->dismissed = true;
159 | break;
160 |
161 | case 'click':
162 | $sp->clicked = true;
163 | break;
164 | }
165 |
166 | return $this->asJson([
167 | $campaign_id, $endpoint, $action, $sp->save()
168 | ]);
169 | }
170 |
171 | public function actionServiceWorker(): string
172 | {
173 | Yii::$app->response->format = Response::FORMAT_RAW;
174 | Yii::$app->response->headers->add('Content-Type', 'application/javascript');
175 |
176 | return file_get_contents(__DIR__ . '/../assets/sw.js');
177 | }
178 |
179 | public function actionPush($id)
180 | {
181 | $campaign = WpnCampaign::findOne($id);
182 | if (!$campaign) {
183 | throw new NotFoundHttpException("Unknown Campaign");
184 | }
185 |
186 | /** @var Pusher $pusher */
187 | $pusher = $this->module->get('pusher');
188 |
189 | $pusher->sendPush($campaign);
190 | }
191 | }
--------------------------------------------------------------------------------
/src/exceptions/InvalidApplication.php:
--------------------------------------------------------------------------------
1 | createTable('{{%wpn_app}}', [
16 | 'id' => $this->primaryKey(),
17 | 'name' => $this->string()->notNull(),
18 | 'host' => $this->string(180)->unique()->notNull(),
19 | 'private_key' => $this->string(50)->notNull(),
20 | 'public_key' => $this->string(100)->notNull(),
21 | 'subject' => $this->string(255)->notNull(),
22 | 'enabled' => $this->boolean()->notNull(),
23 | 'icon' => $this->string()->null(),
24 | 'badge' => $this->string()->null(),
25 | 'created_at' => $this->dateTime()->notNull(),
26 | 'updated_at' => $this->dateTime()->notNull(),
27 | ], 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB');
28 |
29 | $this->createTable('{{%wpn_subscription}}', [
30 | 'id' => $this->primaryKey(),
31 | 'endpoint' => $this->string(255)->notNull(),
32 | 'auth' => $this->string()->notNull(),
33 | 'public_key' => $this->string()->notNull(),
34 | 'content_encoding' => $this->string()->notNull(),
35 | 'subscribed' => $this->boolean()->notNull(),
36 | 'test_user' => $this->boolean(),
37 | 'yii_user_id' => $this->integer(),
38 | 'app_id' => $this->integer()->notNull(),
39 | 'ua' => $this->string(),
40 | 'ip' => $this->string()->notNull(),
41 | 'os' => $this->string(),
42 | 'browser' => $this->string(),
43 | 'last_seen' => $this->dateTime(),
44 | 'last_error' => $this->string(),
45 | 'reason' => $this->string(),
46 | 'created_at' => $this->dateTime()->notNull(),
47 | 'updated_at' => $this->dateTime()->notNull(),
48 | ], 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB');
49 | $this->createIndex('uq_wpn_subscription_endpoint', '{{%wpn_subscription}}', 'endpoint', true);
50 | $this->createIndex('uq_wpn_subscription_yii_user_id', '{{%wpn_subscription}}', 'yii_user_id');
51 | $this->createIndex('uq_wpn_subscription_subscribed', '{{%wpn_subscription}}', 'subscribed');
52 | $this->createIndex('uq_wpn_subscription_app_id', '{{%wpn_subscription}}', 'app_id');
53 |
54 | $this->addForeignKey('fk_wpn_subscription_app', '{{%wpn_subscription}}', 'app_id', '{{%wpn_app}}', 'id');
55 |
56 | $this->createTable('{{%wpn_campaign}}', [
57 | 'id' => $this->primaryKey(),
58 | 'app_id' => $this->integer()->notNull(),
59 | 'title' => $this->string()->notNull(),
60 | 'tag' => $this->string(180)->unique()->notNull(),
61 | 'body' => $this->string()->notNull(),
62 | 'url' => $this->string(),
63 | 'image' => $this->string(),
64 | 'test_only' => $this->boolean(),
65 | 'created_at' => $this->dateTime()->notNull(),
66 | 'scheduled_at' => $this->dateTime()->notNull(),
67 | 'started_at' => $this->dateTime(),
68 | 'finished_at' => $this->dateTime(),
69 | 'updated_at' => $this->dateTime()->notNull(),
70 | 'extra' => $this->text(),
71 | ]);
72 | $this->createIndex('uq_wpn_campaign_app_id', '{{%wpn_campaign}}', 'app_id');
73 | $this->addForeignKey('fk_wpn_campaign_app', '{{%wpn_campaign}}', 'app_id', '{{%wpn_app}}', 'id');
74 |
75 | $this->createTable('{{%wpn_report}}', [
76 | 'id' => $this->primaryKey(),
77 | 'campaign_id' => $this->integer()->notNull(),
78 | 'subscription_id' => $this->integer()->notNull(),
79 | 'sent_at' => $this->dateTime()->notNull(),
80 | 'received' => $this->boolean(),
81 | 'viewed' => $this->boolean(),
82 | 'clicked' => $this->boolean(),
83 | 'dismissed' => $this->boolean(),
84 | ]);
85 | $this->createIndex('uq_wpn_report', '{{%wpn_report}}', ['subscription_id', 'campaign_id'], true);
86 |
87 | $this->createIndex('idx_wpn_report_campaign', '{{%wpn_report}}','campaign_id');
88 | $this->addForeignKey('fk_wpn_report_campaign', '{{%wpn_report}}','campaign_id', '{{%wpn_campaign}}', 'id');
89 |
90 | $this->createIndex('idx_wpn_report_subscription', '{{%wpn_report}}','subscription_id');
91 | $this->addForeignKey('fk_wpn_report_subscription', '{{%wpn_report}}','subscription_id', '{{%wpn_subscription}}', 'id');
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function safeDown()
98 | {
99 | $this->dropTable('{{%wpn_report}}');
100 | $this->dropTable('{{%wpn_campaign}}');
101 | $this->dropTable('{{%wpn_subscription}}');
102 | $this->dropTable('{{%wpn_app}}');
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/models/WpnApp.php:
--------------------------------------------------------------------------------
1 | TimestampBehavior::class,
39 | 'attributes' => [
40 | self::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
41 | self::EVENT_BEFORE_UPDATE => ['updated_at'],
42 | ],
43 | 'value' => new Expression('NOW()'),
44 | ],
45 | ];
46 | }
47 |
48 | public function rules(): array
49 | {
50 | return [
51 | [['name', 'host', 'enabled', 'public_key', 'private_key', 'subject'], 'required'],
52 | [['name', 'subject', 'icon', 'badge'], 'string', 'max' => 255],
53 | [['host'], 'string', 'max' => 180],
54 | [['private_key'], 'string', 'max' => 50],
55 | [['public_key'], 'string', 'max' => 100],
56 | [['enabled'], 'boolean'],
57 | [['host'], 'unique'],
58 | ];
59 | }
60 |
61 | public function getCampaigns(): \yii\db\ActiveQuery
62 | {
63 | return $this->hasMany(WpnCampaign::class, ['app_id' => 'id']);
64 | }
65 |
66 | public function getSubscriptions(): \yii\db\ActiveQuery
67 | {
68 | return $this->hasMany(WpnSubscription::class, ['app_id' => 'id']);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/models/WpnCampaign.php:
--------------------------------------------------------------------------------
1 | TimestampBehavior::class,
45 | 'attributes' => [
46 | self::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
47 | self::EVENT_BEFORE_UPDATE => ['updated_at'],
48 | ],
49 | 'value' => new Expression('NOW()'),
50 | ],
51 | ];
52 | }
53 |
54 | public function rules(): array
55 | {
56 | return [
57 | [['title', 'body', 'scheduled_at', 'tag', 'app_id'], 'required'],
58 | [['app_id'], 'integer'],
59 | [['scheduled_at', 'started_at', 'finished_at'], 'datetime', 'format' => 'php:Y-m-d H:i:s'],
60 | [['extra'], 'string'],
61 | [['test_only'], 'boolean'],
62 | [['title', 'body', 'url', 'image', 'tag'], 'string', 'max' => 255],
63 | [['app_id'], 'exist', 'skipOnError' => true, 'targetClass' => WpnApp::class, 'targetAttribute' => ['app_id' => 'id']],
64 | ];
65 | }
66 |
67 | public function getReports(): \yii\db\ActiveQuery
68 | {
69 | return $this->hasMany(WpnReport::class, ['campaign_id' => 'id']);
70 | }
71 |
72 | public function getSubscriptions(): \yii\db\ActiveQuery
73 | {
74 | return $this->hasMany(WpnSubscription::class, ['id' => 'subscription_id'])->via('reports');
75 | }
76 |
77 | public function getApp(): \yii\db\ActiveQuery
78 | {
79 | return $this->hasOne(WpnApp::class, ['id' => 'app_id']);
80 | }
81 |
82 | public function getOptions(): array
83 | {
84 | $options = [
85 | 'title' => $this->title,
86 | 'body' => $this->body,
87 | 'tag' => $this->tag,
88 | 'data' => [
89 | 'url' => $this->url,
90 | 'campaignId' => $this->id,
91 | ],
92 | ];
93 |
94 | if ($this->image) {
95 | $options['image'] = $this->image;
96 | }
97 |
98 | if ($this->app->badge) {
99 | $options['badge'] = $this->app->badge;
100 | }
101 |
102 | if ($this->app->icon) {
103 | $options['icon'] = $this->app->icon;
104 | }
105 |
106 | return $options;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/models/WpnReport.php:
--------------------------------------------------------------------------------
1 | 'php:Y-m-d H:i:s'],
36 | [['received', 'viewed', 'clicked', 'dismissed'], 'boolean'],
37 | [['subscription_id', 'campaign_id'], 'unique', 'targetAttribute' => ['subscription_id', 'campaign_id']],
38 | [['campaign_id'], 'exist', 'skipOnError' => true, 'targetClass' => WpnCampaign::class, 'targetAttribute' => ['campaign_id' => 'id']],
39 | [['subscription_id'], 'exist', 'skipOnError' => true, 'targetClass' => WpnSubscription::class, 'targetAttribute' => ['subscription_id' => 'id']],
40 | ];
41 | }
42 |
43 | public function getCampaign(): \yii\db\ActiveQuery
44 | {
45 | return $this->hasOne(WpnCampaign::class, ['id' => 'campaign_id']);
46 | }
47 |
48 | public function getSubscription(): \yii\db\ActiveQuery
49 | {
50 | return $this->hasOne(WpnSubscription::class, ['id' => 'subscription_id']);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/models/WpnSubscription.php:
--------------------------------------------------------------------------------
1 | TimestampBehavior::class,
49 | 'attributes' => [
50 | self::EVENT_BEFORE_INSERT => ['created_at', 'last_seen', 'updated_at'],
51 | self::EVENT_BEFORE_UPDATE => ['updated_at', 'last_seen'],
52 | ],
53 | 'value' => new Expression('NOW()'),
54 | ],
55 | ];
56 | }
57 |
58 | public function rules(): array
59 | {
60 | return [
61 | [['endpoint', 'auth', 'public_key', 'content_encoding', 'subscribed', 'ip', 'app_id'], 'required'],
62 | [['yii_user_id', 'app_id'], 'integer'],
63 | [['endpoint', 'auth', 'public_key', 'content_encoding', 'ua', 'ip', 'os', 'browser', 'last_error', 'reason'], 'string', 'max' => 255],
64 | [['subscribed', 'test_user'], 'boolean'],
65 | [['endpoint'], 'unique'],
66 | [['app_id'], 'exist', 'skipOnError' => true, 'targetClass' => WpnApp::class, 'targetAttribute' => ['app_id' => 'id']],
67 | ];
68 | }
69 |
70 | public function getReports(): \yii\db\ActiveQuery
71 | {
72 | return $this->hasMany(WpnReport::class, ['subscription_id' => 'id']);
73 | }
74 |
75 | public function getCampaigns(): \yii\db\ActiveQuery
76 | {
77 | return $this->hasMany(WpnCampaign::class, ['id' => 'campaign_id'])->via('reports');
78 | }
79 |
80 | public function getApp(): \yii\db\ActiveQuery
81 | {
82 | return $this->hasOne(WpnApp::class, ['id' => 'app_id']);
83 | }
84 |
85 | public function setEndpoint($value)
86 | {
87 | $this->endpoint = $value;
88 | }
89 |
90 | public function getEndpoint(): string
91 | {
92 | return $this->endpoint;
93 | }
94 |
95 | public function getPublicKey(): ?string
96 | {
97 | return $this->public_key;
98 | }
99 |
100 | public function getAuthToken(): ?string
101 | {
102 | return $this->auth;
103 | }
104 |
105 | public function getContentEncoding(): ?string
106 | {
107 | return $this->content_encoding;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/widgets/SubscriptionWidget.php:
--------------------------------------------------------------------------------
1 | app || !is_a($this->app, WpnApp::class)) {
23 | throw new InvalidConfigException('A valid WpnApp must be provided in the $app property');
24 | }
25 | parent::init();
26 | }
27 |
28 | public function run(): string
29 | {
30 | if (!$this->app->enabled) {
31 | return '';
32 | }
33 |
34 | return $this->render('subscription', [
35 | 'app' => $this->app,
36 | 'shouldMigrate' => $this->shouldMigrate,
37 | ]);
38 | }
39 | }
--------------------------------------------------------------------------------
/src/widgets/views/subscription.php:
--------------------------------------------------------------------------------
1 | registerJs(<< {
13 | var wp = new WebPush({$app->id}, "{$app->public_key}");
14 | var wpnStatus = jQuery('.wpn-status');
15 | jQuery(".wpn-subscribe").on("click", function() {
16 | wp.subscribe(function(subscription) {
17 | wpnStatus.text("Subscribed, endpoint is" + subscription.endpoint);
18 | }, function (error) {
19 | wpnStatus.text("Got error: " + error);
20 | });
21 | });
22 |
23 | jQuery(".wpn-unsubscribe").on("click", function() {
24 | wp.unsubscribe(function () {
25 | wpnStatus.text("Unsubscribed");
26 | }, function () {
27 | wpnStatus.text("Error while unsubscribing");
28 | });
29 | });
30 |
31 | wp.setupRegistration("/sw.js", function(status) {
32 | wpnStatus.text(status);
33 | }, function(error) {
34 | wpnStatus.text("error");
35 | console.error(error);
36 | }, $shouldMigrate);
37 |
38 |
39 | });
40 | JS
41 | );
42 |
43 | ?>
44 |
45 |
46 |
unknown
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/tests/PusherTest.php:
--------------------------------------------------------------------------------
1 | tester->haveFixtures([
27 | 'app' => [
28 | 'class' => WpnAppFixture::class,
29 | ],
30 | 'campaign' => [
31 | 'class' => WpnCampaignFixture::class,
32 | ],
33 | 'subscription' => [
34 | 'class' => WpnSubscriptionFixture::class,
35 | ],
36 | ]);
37 |
38 | $stub = $this->make(WebPush::class, [
39 | 'queueNotification' => function() {
40 |
41 | },
42 | 'flush' => function() {
43 | return [1];
44 | }
45 | ]);
46 | \Yii::$container->set(WebPush::class, $stub);
47 |
48 |
49 | $module = Module::getInstance();
50 | $this->pusher = $module->get('pusher');
51 |
52 | }
53 |
54 | // tests
55 | public function testRefusingDisabledApplication()
56 | {
57 | $campaign = $this->tester->grabFixture('campaign', 1);
58 | $this->assertThrows(InvalidApplication::class, function() use ($campaign) {
59 | $this->pusher->sendPush($campaign);
60 | });
61 | }
62 |
63 |
64 | public function testMock()
65 | {
66 | $campaign = $this->tester->grabFixture('campaign', 0);
67 |
68 | $this->pusher->sendPush($campaign);
69 | }
70 | }
--------------------------------------------------------------------------------
/tests/WebPushMock.php:
--------------------------------------------------------------------------------
1 | notifications[] = new Notification($subscription, $payload, $options, $auth);
19 | }
20 |
21 | public function flush(?int $batchSize = null): \Generator
22 | {
23 | if (null === $this->notifications || empty($this->notifications)) {
24 | yield from [];
25 | return;
26 | }
27 |
28 | if (null === $batchSize) {
29 | $batchSize = $this->defaultOptions['batchSize'];
30 | }
31 |
32 | $batches = array_chunk($this->notifications, $batchSize);
33 |
34 | // reset queue
35 | $this->notifications = [];
36 |
37 | foreach ($batches as $batch) {
38 | // for each endpoint server type
39 | $requests = $this->prepare($batch);
40 |
41 | $promises = [];
42 |
43 | foreach ($requests as $request) {
44 | $promises[] = $this->client->sendAsync($request)
45 | ->then(function ($response) use ($request) {
46 | /** @var ResponseInterface $response * */
47 | return new MessageSentReport($request, $response);
48 | })
49 | ->otherwise(function ($reason) {
50 | /** @var RequestException $reason **/
51 | if (method_exists($reason, 'getResponse')) {
52 | $response = $reason->getResponse();
53 | } else {
54 | $response = null;
55 | }
56 | return new MessageSentReport($reason->getRequest(), $response, false, $reason->getMessage());
57 | });
58 | }
59 |
60 | foreach ($promises as $promise) {
61 | yield $promise->wait();
62 | }
63 | }
64 |
65 | if ($this->reuseVAPIDHeaders) {
66 | $this->vapidHeaders = [];
67 | }
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/tests/_bootstrap.php:
--------------------------------------------------------------------------------
1 | haveFixtures([
25 | * 'posts' => PostsFixture::className(),
26 | * 'user' => [
27 | * 'class' => UserFixture::className(),
28 | * 'dataFile' => '@tests/_data/models/user.php',
29 | * ],
30 | * ]);
31 | * ```
32 | *
33 | * Note: if you need to load fixtures before a test (probably before the
34 | * cleanup transaction is started; `cleanup` option is `true` by default),
35 | * you can specify the fixtures in the `_fixtures()` method of a test case
36 | *
37 | * ```php
38 | * [
43 | * 'class' => UserFixture::className(),
44 | * 'dataFile' => codecept_data_dir() . 'user.php'
45 | * ]
46 | * ];
47 | * }
48 | * ```
49 | * instead of calling `haveFixtures` in Cest `_before`
50 | *
51 | * @param $fixtures
52 | * @part fixtures
53 | * @see \Codeception\Module\Yii2::haveFixtures()
54 | */
55 | public function haveFixtures($fixtures) {
56 | return $this->getScenario()->runStep(new \Codeception\Step\Action('haveFixtures', func_get_args()));
57 | }
58 |
59 |
60 | /**
61 | * [!] Method is generated. Documentation taken from corresponding module.
62 | *
63 | * Returns all loaded fixtures.
64 | * Array of fixture instances
65 | *
66 | * @part fixtures
67 | * @return array
68 | * @see \Codeception\Module\Yii2::grabFixtures()
69 | */
70 | public function grabFixtures() {
71 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFixtures', func_get_args()));
72 | }
73 |
74 |
75 | /**
76 | * [!] Method is generated. Documentation taken from corresponding module.
77 | *
78 | * Gets a fixture by name.
79 | * Returns a Fixture instance. If a fixture is an instance of
80 | * `\yii\test\BaseActiveFixture` a second parameter can be used to return a
81 | * specific model:
82 | *
83 | * ```php
84 | * haveFixtures(['users' => UserFixture::className()]);
86 | *
87 | * $users = $I->grabFixture('users');
88 | *
89 | * // get first user by key, if a fixture is an instance of ActiveFixture
90 | * $user = $I->grabFixture('users', 'user1');
91 | * ```
92 | *
93 | * @param $name
94 | * @return mixed
95 | * @throws ModuleException if the fixture is not found
96 | * @part fixtures
97 | * @see \Codeception\Module\Yii2::grabFixture()
98 | */
99 | public function grabFixture($name, $index = NULL) {
100 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFixture', func_get_args()));
101 | }
102 |
103 |
104 | /**
105 | * [!] Method is generated. Documentation taken from corresponding module.
106 | *
107 | * Inserts a record into the database.
108 | *
109 | * ``` php
110 | * haveRecord('app\models\User', array('name' => 'Davert'));
112 | * ?>
113 | * ```
114 | *
115 | * @param $model
116 | * @param array $attributes
117 | * @return mixed
118 | * @part orm
119 | * @see \Codeception\Module\Yii2::haveRecord()
120 | */
121 | public function haveRecord($model, $attributes = []) {
122 | return $this->getScenario()->runStep(new \Codeception\Step\Action('haveRecord', func_get_args()));
123 | }
124 |
125 |
126 | /**
127 | * [!] Method is generated. Documentation taken from corresponding module.
128 | *
129 | * Checks that a record exists in the database.
130 | *
131 | * ``` php
132 | * $I->seeRecord('app\models\User', array('name' => 'davert'));
133 | * ```
134 | *
135 | * @param $model
136 | * @param array $attributes
137 | * @part orm
138 | * @see \Codeception\Module\Yii2::seeRecord()
139 | */
140 | public function seeRecord($model, $attributes = []) {
141 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeRecord', func_get_args()));
142 | }
143 |
144 |
145 | /**
146 | * [!] Method is generated. Documentation taken from corresponding module.
147 | *
148 | * Checks that a record does not exist in the database.
149 | *
150 | * ``` php
151 | * $I->dontSeeRecord('app\models\User', array('name' => 'davert'));
152 | * ```
153 | *
154 | * @param $model
155 | * @param array $attributes
156 | * @part orm
157 | * @see \Codeception\Module\Yii2::dontSeeRecord()
158 | */
159 | public function dontSeeRecord($model, $attributes = []) {
160 | return $this->getScenario()->runStep(new \Codeception\Step\Action('dontSeeRecord', func_get_args()));
161 | }
162 |
163 |
164 | /**
165 | * [!] Method is generated. Documentation taken from corresponding module.
166 | *
167 | * Retrieves a record from the database
168 | *
169 | * ``` php
170 | * $category = $I->grabRecord('app\models\User', array('name' => 'davert'));
171 | * ```
172 | *
173 | * @param $model
174 | * @param array $attributes
175 | * @return mixed
176 | * @part orm
177 | * @see \Codeception\Module\Yii2::grabRecord()
178 | */
179 | public function grabRecord($model, $attributes = []) {
180 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabRecord', func_get_args()));
181 | }
182 |
183 |
184 | /**
185 | * [!] Method is generated. Documentation taken from corresponding module.
186 | *
187 | * Handles and checks exception called inside callback function.
188 | * Either exception class name or exception instance should be provided.
189 | *
190 | * ```php
191 | * expectException(MyException::class, function() {
193 | * $this->doSomethingBad();
194 | * });
195 | *
196 | * $I->expectException(new MyException(), function() {
197 | * $this->doSomethingBad();
198 | * });
199 | * ```
200 | * If you want to check message or exception code, you can pass them with exception instance:
201 | * ```php
202 | * expectException(new MyException("Don't do bad things"), function() {
205 | * $this->doSomethingBad();
206 | * });
207 | * ```
208 | *
209 | * @deprecated Use expectThrowable() instead
210 | * @param \Exception|string $exception
211 | * @param callable $callback
212 | * @see \Codeception\Module\Asserts::expectException()
213 | */
214 | public function expectException($exception, $callback) {
215 | return $this->getScenario()->runStep(new \Codeception\Step\Action('expectException', func_get_args()));
216 | }
217 |
218 |
219 | /**
220 | * [!] Method is generated. Documentation taken from corresponding module.
221 | *
222 | * Handles and checks throwables (Exceptions/Errors) called inside the callback function.
223 | * Either throwable class name or throwable instance should be provided.
224 | *
225 | * ```php
226 | * expectThrowable(MyThrowable::class, function() {
228 | * $this->doSomethingBad();
229 | * });
230 | *
231 | * $I->expectThrowable(new MyException(), function() {
232 | * $this->doSomethingBad();
233 | * });
234 | * ```
235 | * If you want to check message or throwable code, you can pass them with throwable instance:
236 | * ```php
237 | * expectThrowable(new MyError("Don't do bad things"), function() {
240 | * $this->doSomethingBad();
241 | * });
242 | * ```
243 | *
244 | * @param \Throwable|string $throwable
245 | * @param callable $callback
246 | * @see \Codeception\Module\Asserts::expectThrowable()
247 | */
248 | public function expectThrowable($throwable, $callback) {
249 | return $this->getScenario()->runStep(new \Codeception\Step\Action('expectThrowable', func_get_args()));
250 | }
251 |
252 |
253 | /**
254 | * [!] Method is generated. Documentation taken from corresponding module.
255 | *
256 | * Asserts that a file does not exist.
257 | *
258 | * @param string $filename
259 | * @param string $message
260 | * @see \Codeception\Module\AbstractAsserts::assertFileNotExists()
261 | */
262 | public function assertFileNotExists($filename, $message = "") {
263 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotExists', func_get_args()));
264 | }
265 |
266 |
267 | /**
268 | * [!] Method is generated. Documentation taken from corresponding module.
269 | *
270 | * Asserts that a value is greater than or equal to another value.
271 | *
272 | * @param $expected
273 | * @param $actual
274 | * @param string $message
275 | * @see \Codeception\Module\AbstractAsserts::assertGreaterOrEquals()
276 | */
277 | public function assertGreaterOrEquals($expected, $actual, $message = "") {
278 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterOrEquals', func_get_args()));
279 | }
280 |
281 |
282 | /**
283 | * [!] Method is generated. Documentation taken from corresponding module.
284 | *
285 | * Asserts that a variable is empty.
286 | *
287 | * @param $actual
288 | * @param string $message
289 | * @see \Codeception\Module\AbstractAsserts::assertIsEmpty()
290 | */
291 | public function assertIsEmpty($actual, $message = "") {
292 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsEmpty', func_get_args()));
293 | }
294 |
295 |
296 | /**
297 | * [!] Method is generated. Documentation taken from corresponding module.
298 | *
299 | * Asserts that a value is smaller than or equal to another value.
300 | *
301 | * @param $expected
302 | * @param $actual
303 | * @param string $message
304 | * @see \Codeception\Module\AbstractAsserts::assertLessOrEquals()
305 | */
306 | public function assertLessOrEquals($expected, $actual, $message = "") {
307 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessOrEquals', func_get_args()));
308 | }
309 |
310 |
311 | /**
312 | * [!] Method is generated. Documentation taken from corresponding module.
313 | *
314 | * Asserts that a string does not match a given regular expression.
315 | *
316 | * @param string $pattern
317 | * @param string $string
318 | * @param string $message
319 | * @see \Codeception\Module\AbstractAsserts::assertNotRegExp()
320 | */
321 | public function assertNotRegExp($pattern, $string, $message = "") {
322 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotRegExp', func_get_args()));
323 | }
324 |
325 |
326 | /**
327 | * [!] Method is generated. Documentation taken from corresponding module.
328 | *
329 | * Asserts that a string matches a given regular expression.
330 | *
331 | * @param string $pattern
332 | * @param string $string
333 | * @param string $message
334 | * @see \Codeception\Module\AbstractAsserts::assertRegExp()
335 | */
336 | public function assertRegExp($pattern, $string, $message = "") {
337 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertRegExp', func_get_args()));
338 | }
339 |
340 |
341 | /**
342 | * [!] Method is generated. Documentation taken from corresponding module.
343 | *
344 | * Evaluates a PHPUnit\Framework\Constraint matcher object.
345 | *
346 | * @param $value
347 | * @param Constraint $constraint
348 | * @param string $message
349 | * @see \Codeception\Module\AbstractAsserts::assertThatItsNot()
350 | */
351 | public function assertThatItsNot($value, $constraint, $message = "") {
352 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertThatItsNot', func_get_args()));
353 | }
354 |
355 |
356 | /**
357 | * [!] Method is generated. Documentation taken from corresponding module.
358 | *
359 | * Asserts that an array has a specified key.
360 | *
361 | * @param int|string $key
362 | * @param array|ArrayAccess $array
363 | * @param string $message
364 | * @see \Codeception\Module\AbstractAsserts::assertArrayHasKey()
365 | */
366 | public function assertArrayHasKey($key, $array, $message = "") {
367 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertArrayHasKey', func_get_args()));
368 | }
369 |
370 |
371 | /**
372 | * [!] Method is generated. Documentation taken from corresponding module.
373 | *
374 | * Asserts that an array does not have a specified key.
375 | *
376 | * @param int|string $key
377 | * @param array|ArrayAccess $array
378 | * @param string $message
379 | * @see \Codeception\Module\AbstractAsserts::assertArrayNotHasKey()
380 | */
381 | public function assertArrayNotHasKey($key, $array, $message = "") {
382 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertArrayNotHasKey', func_get_args()));
383 | }
384 |
385 |
386 | /**
387 | * [!] Method is generated. Documentation taken from corresponding module.
388 | *
389 | * Asserts that a class has a specified attribute.
390 | *
391 | * @param string $attributeName
392 | * @param string $className
393 | * @param string $message
394 | * @see \Codeception\Module\AbstractAsserts::assertClassHasAttribute()
395 | */
396 | public function assertClassHasAttribute($attributeName, $className, $message = "") {
397 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertClassHasAttribute', func_get_args()));
398 | }
399 |
400 |
401 | /**
402 | * [!] Method is generated. Documentation taken from corresponding module.
403 | *
404 | * Asserts that a class has a specified static attribute.
405 | *
406 | * @param string $attributeName
407 | * @param string $className
408 | * @param string $message
409 | * @see \Codeception\Module\AbstractAsserts::assertClassHasStaticAttribute()
410 | */
411 | public function assertClassHasStaticAttribute($attributeName, $className, $message = "") {
412 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertClassHasStaticAttribute', func_get_args()));
413 | }
414 |
415 |
416 | /**
417 | * [!] Method is generated. Documentation taken from corresponding module.
418 | *
419 | * Asserts that a class does not have a specified attribute.
420 | *
421 | * @param string $attributeName
422 | * @param string $className
423 | * @param string $message
424 | * @see \Codeception\Module\AbstractAsserts::assertClassNotHasAttribute()
425 | */
426 | public function assertClassNotHasAttribute($attributeName, $className, $message = "") {
427 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertClassNotHasAttribute', func_get_args()));
428 | }
429 |
430 |
431 | /**
432 | * [!] Method is generated. Documentation taken from corresponding module.
433 | *
434 | * Asserts that a class does not have a specified static attribute.
435 | *
436 | * @param string $attributeName
437 | * @param string $className
438 | * @param string $message
439 | * @see \Codeception\Module\AbstractAsserts::assertClassNotHasStaticAttribute()
440 | */
441 | public function assertClassNotHasStaticAttribute($attributeName, $className, $message = "") {
442 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertClassNotHasStaticAttribute', func_get_args()));
443 | }
444 |
445 |
446 | /**
447 | * [!] Method is generated. Documentation taken from corresponding module.
448 | *
449 | * Asserts that a haystack contains a needle.
450 | *
451 | * @param $needle
452 | * @param $haystack
453 | * @param string $message
454 | * @see \Codeception\Module\AbstractAsserts::assertContains()
455 | */
456 | public function assertContains($needle, $haystack, $message = "") {
457 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContains', func_get_args()));
458 | }
459 |
460 |
461 | /**
462 | * [!] Method is generated. Documentation taken from corresponding module.
463 | *
464 | * @param $needle
465 | * @param $haystack
466 | * @param string $message
467 | * @see \Codeception\Module\AbstractAsserts::assertContainsEquals()
468 | */
469 | public function assertContainsEquals($needle, $haystack, $message = "") {
470 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContainsEquals', func_get_args()));
471 | }
472 |
473 |
474 | /**
475 | * [!] Method is generated. Documentation taken from corresponding module.
476 | *
477 | * Asserts that a haystack contains only values of a given type.
478 | *
479 | * @param string $type
480 | * @param $haystack
481 | * @param bool|null $isNativeType
482 | * @param string $message
483 | * @see \Codeception\Module\AbstractAsserts::assertContainsOnly()
484 | */
485 | public function assertContainsOnly($type, $haystack, $isNativeType = NULL, $message = "") {
486 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContainsOnly', func_get_args()));
487 | }
488 |
489 |
490 | /**
491 | * [!] Method is generated. Documentation taken from corresponding module.
492 | *
493 | * Asserts that a haystack contains only instances of a given class name.
494 | *
495 | * @param string $className
496 | * @param $haystack
497 | * @param string $message
498 | * @see \Codeception\Module\AbstractAsserts::assertContainsOnlyInstancesOf()
499 | */
500 | public function assertContainsOnlyInstancesOf($className, $haystack, $message = "") {
501 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContainsOnlyInstancesOf', func_get_args()));
502 | }
503 |
504 |
505 | /**
506 | * [!] Method is generated. Documentation taken from corresponding module.
507 | *
508 | * Asserts the number of elements of an array, Countable or Traversable.
509 | *
510 | * @param int $expectedCount
511 | * @param Countable|iterable $haystack
512 | * @param string $message
513 | * @see \Codeception\Module\AbstractAsserts::assertCount()
514 | */
515 | public function assertCount($expectedCount, $haystack, $message = "") {
516 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertCount', func_get_args()));
517 | }
518 |
519 |
520 | /**
521 | * [!] Method is generated. Documentation taken from corresponding module.
522 | *
523 | * Asserts that a directory does not exist.
524 | *
525 | * @param string $directory
526 | * @param string $message
527 | * @see \Codeception\Module\AbstractAsserts::assertDirectoryDoesNotExist()
528 | */
529 | public function assertDirectoryDoesNotExist($directory, $message = "") {
530 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDirectoryDoesNotExist', func_get_args()));
531 | }
532 |
533 |
534 | /**
535 | * [!] Method is generated. Documentation taken from corresponding module.
536 | *
537 | * Asserts that a directory exists.
538 | *
539 | * @param string $directory
540 | * @param string $message
541 | * @see \Codeception\Module\AbstractAsserts::assertDirectoryExists()
542 | */
543 | public function assertDirectoryExists($directory, $message = "") {
544 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDirectoryExists', func_get_args()));
545 | }
546 |
547 |
548 | /**
549 | * [!] Method is generated. Documentation taken from corresponding module.
550 | *
551 | * Asserts that a directory exists and is not readable.
552 | *
553 | * @param string $directory
554 | * @param string $message
555 | * @see \Codeception\Module\AbstractAsserts::assertDirectoryIsNotReadable()
556 | */
557 | public function assertDirectoryIsNotReadable($directory, $message = "") {
558 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDirectoryIsNotReadable', func_get_args()));
559 | }
560 |
561 |
562 | /**
563 | * [!] Method is generated. Documentation taken from corresponding module.
564 | *
565 | * Asserts that a directory exists and is not writable.
566 | *
567 | * @param string $directory
568 | * @param string $message
569 | * @see \Codeception\Module\AbstractAsserts::assertDirectoryIsNotWritable()
570 | */
571 | public function assertDirectoryIsNotWritable($directory, $message = "") {
572 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDirectoryIsNotWritable', func_get_args()));
573 | }
574 |
575 |
576 | /**
577 | * [!] Method is generated. Documentation taken from corresponding module.
578 | *
579 | * Asserts that a directory exists and is readable.
580 | *
581 | * @param string $directory
582 | * @param string $message
583 | * @see \Codeception\Module\AbstractAsserts::assertDirectoryIsReadable()
584 | */
585 | public function assertDirectoryIsReadable($directory, $message = "") {
586 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDirectoryIsReadable', func_get_args()));
587 | }
588 |
589 |
590 | /**
591 | * [!] Method is generated. Documentation taken from corresponding module.
592 | *
593 | * Asserts that a directory exists and is writable.
594 | *
595 | * @param string $directory
596 | * @param string $message
597 | * @see \Codeception\Module\AbstractAsserts::assertDirectoryIsWritable()
598 | */
599 | public function assertDirectoryIsWritable($directory, $message = "") {
600 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDirectoryIsWritable', func_get_args()));
601 | }
602 |
603 |
604 | /**
605 | * [!] Method is generated. Documentation taken from corresponding module.
606 | *
607 | * Asserts that a string does not match a given regular expression.
608 | *
609 | * @param string $pattern
610 | * @param string $string
611 | * @param string $message
612 | * @see \Codeception\Module\AbstractAsserts::assertDoesNotMatchRegularExpression()
613 | */
614 | public function assertDoesNotMatchRegularExpression($pattern, $string, $message = "") {
615 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertDoesNotMatchRegularExpression', func_get_args()));
616 | }
617 |
618 |
619 | /**
620 | * [!] Method is generated. Documentation taken from corresponding module.
621 | *
622 | * Asserts that a variable is empty.
623 | *
624 | * @param $actual
625 | * @param string $message
626 | * @see \Codeception\Module\AbstractAsserts::assertEmpty()
627 | */
628 | public function assertEmpty($actual, $message = "") {
629 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEmpty', func_get_args()));
630 | }
631 |
632 |
633 | /**
634 | * [!] Method is generated. Documentation taken from corresponding module.
635 | *
636 | * Asserts that two variables are equal.
637 | *
638 | * @param $expected
639 | * @param $actual
640 | * @param string $message
641 | * @see \Codeception\Module\AbstractAsserts::assertEquals()
642 | */
643 | public function assertEquals($expected, $actual, $message = "") {
644 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEquals', func_get_args()));
645 | }
646 |
647 |
648 | /**
649 | * [!] Method is generated. Documentation taken from corresponding module.
650 | *
651 | * Asserts that two variables are equal (canonicalizing).
652 | *
653 | * @param $expected
654 | * @param $actual
655 | * @param string $message
656 | * @see \Codeception\Module\AbstractAsserts::assertEqualsCanonicalizing()
657 | */
658 | public function assertEqualsCanonicalizing($expected, $actual, $message = "") {
659 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsCanonicalizing', func_get_args()));
660 | }
661 |
662 |
663 | /**
664 | * [!] Method is generated. Documentation taken from corresponding module.
665 | *
666 | * Asserts that two variables are equal (ignoring case).
667 | *
668 | * @param $expected
669 | * @param $actual
670 | * @param string $message
671 | * @see \Codeception\Module\AbstractAsserts::assertEqualsIgnoringCase()
672 | */
673 | public function assertEqualsIgnoringCase($expected, $actual, $message = "") {
674 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsIgnoringCase', func_get_args()));
675 | }
676 |
677 |
678 | /**
679 | * [!] Method is generated. Documentation taken from corresponding module.
680 | *
681 | * Asserts that two variables are equal (with delta).
682 | *
683 | * @param $expected
684 | * @param $actual
685 | * @param float $delta
686 | * @param string $message
687 | * @see \Codeception\Module\AbstractAsserts::assertEqualsWithDelta()
688 | */
689 | public function assertEqualsWithDelta($expected, $actual, $delta, $message = "") {
690 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertEqualsWithDelta', func_get_args()));
691 | }
692 |
693 |
694 | /**
695 | * [!] Method is generated. Documentation taken from corresponding module.
696 | *
697 | * Asserts that a condition is false.
698 | *
699 | * @param $condition
700 | * @param string $message
701 | * @see \Codeception\Module\AbstractAsserts::assertFalse()
702 | */
703 | public function assertFalse($condition, $message = "") {
704 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFalse', func_get_args()));
705 | }
706 |
707 |
708 | /**
709 | * [!] Method is generated. Documentation taken from corresponding module.
710 | *
711 | * Asserts that a file does not exist.
712 | *
713 | * @param string $filename
714 | * @param string $message
715 | * @see \Codeception\Module\AbstractAsserts::assertFileDoesNotExist()
716 | */
717 | public function assertFileDoesNotExist($filename, $message = "") {
718 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileDoesNotExist', func_get_args()));
719 | }
720 |
721 |
722 | /**
723 | * [!] Method is generated. Documentation taken from corresponding module.
724 | *
725 | * Asserts that the contents of one file is equal to the contents of another file.
726 | *
727 | * @param string $expected
728 | * @param string $actual
729 | * @param string $message
730 | * @see \Codeception\Module\AbstractAsserts::assertFileEquals()
731 | */
732 | public function assertFileEquals($expected, $actual, $message = "") {
733 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileEquals', func_get_args()));
734 | }
735 |
736 |
737 | /**
738 | * [!] Method is generated. Documentation taken from corresponding module.
739 | *
740 | * Asserts that the contents of one file is equal to the contents of another file (canonicalizing).
741 | *
742 | * @param $expected
743 | * @param $actual
744 | * @param string $message
745 | * @see \Codeception\Module\AbstractAsserts::assertFileEqualsCanonicalizing()
746 | */
747 | public function assertFileEqualsCanonicalizing($expected, $actual, $message = "") {
748 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileEqualsCanonicalizing', func_get_args()));
749 | }
750 |
751 |
752 | /**
753 | * [!] Method is generated. Documentation taken from corresponding module.
754 | *
755 | * Asserts that the contents of one file is equal to the contents of another file (ignoring case).
756 | *
757 | * @param $expected
758 | * @param $actual
759 | * @param string $message
760 | * @see \Codeception\Module\AbstractAsserts::assertFileEqualsIgnoringCase()
761 | */
762 | public function assertFileEqualsIgnoringCase($expected, $actual, $message = "") {
763 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileEqualsIgnoringCase', func_get_args()));
764 | }
765 |
766 |
767 | /**
768 | * [!] Method is generated. Documentation taken from corresponding module.
769 | *
770 | * Asserts that a file exists.
771 | *
772 | * @param string $filename
773 | * @param string $message
774 | * @see \Codeception\Module\AbstractAsserts::assertFileExists()
775 | */
776 | public function assertFileExists($filename, $message = "") {
777 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileExists', func_get_args()));
778 | }
779 |
780 |
781 | /**
782 | * [!] Method is generated. Documentation taken from corresponding module.
783 | *
784 | * Asserts that a file exists and is not readable.
785 | *
786 | * @param string $file
787 | * @param string $message
788 | * @see \Codeception\Module\AbstractAsserts::assertFileIsNotReadable()
789 | */
790 | public function assertFileIsNotReadable($file, $message = "") {
791 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileIsNotReadable', func_get_args()));
792 | }
793 |
794 |
795 | /**
796 | * [!] Method is generated. Documentation taken from corresponding module.
797 | *
798 | * Asserts that a file exists and is not writable.
799 | *
800 | * @param string $file
801 | * @param string $message
802 | * @see \Codeception\Module\AbstractAsserts::assertFileIsNotWritable()
803 | */
804 | public function assertFileIsNotWritable($file, $message = "") {
805 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileIsNotWritable', func_get_args()));
806 | }
807 |
808 |
809 | /**
810 | * [!] Method is generated. Documentation taken from corresponding module.
811 | *
812 | * Asserts that a file exists and is readable.
813 | *
814 | * @param string $file
815 | * @param string $message
816 | * @see \Codeception\Module\AbstractAsserts::assertFileIsReadable()
817 | */
818 | public function assertFileIsReadable($file, $message = "") {
819 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileIsReadable', func_get_args()));
820 | }
821 |
822 |
823 | /**
824 | * [!] Method is generated. Documentation taken from corresponding module.
825 | *
826 | * Asserts that a file exists and is writable.
827 | *
828 | * @param string $file
829 | * @param string $message
830 | * @see \Codeception\Module\AbstractAsserts::assertFileIsWritable()
831 | */
832 | public function assertFileIsWritable($file, $message = "") {
833 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileIsWritable', func_get_args()));
834 | }
835 |
836 |
837 | /**
838 | * [!] Method is generated. Documentation taken from corresponding module.
839 | *
840 | * Asserts that the contents of one file is not equal to the contents of another file.
841 | *
842 | * @param $expected
843 | * @param $actual
844 | * @param string $message
845 | * @see \Codeception\Module\AbstractAsserts::assertFileNotEquals()
846 | */
847 | public function assertFileNotEquals($expected, $actual, $message = "") {
848 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotEquals', func_get_args()));
849 | }
850 |
851 |
852 | /**
853 | * [!] Method is generated. Documentation taken from corresponding module.
854 | *
855 | * Asserts that the contents of one file is not equal to the contents of another file (canonicalizing).
856 | *
857 | * @param $expected
858 | * @param $actual
859 | * @param string $message
860 | * @see \Codeception\Module\AbstractAsserts::assertFileNotEqualsCanonicalizing()
861 | */
862 | public function assertFileNotEqualsCanonicalizing($expected, $actual, $message = "") {
863 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotEqualsCanonicalizing', func_get_args()));
864 | }
865 |
866 |
867 | /**
868 | * [!] Method is generated. Documentation taken from corresponding module.
869 | *
870 | * Asserts that the contents of one file is not equal to the contents of another file (ignoring case).
871 | *
872 | * @param $expected
873 | * @param $actual
874 | * @param string $message
875 | * @see \Codeception\Module\AbstractAsserts::assertFileNotEqualsIgnoringCase()
876 | */
877 | public function assertFileNotEqualsIgnoringCase($expected, $actual, $message = "") {
878 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFileNotEqualsIgnoringCase', func_get_args()));
879 | }
880 |
881 |
882 | /**
883 | * [!] Method is generated. Documentation taken from corresponding module.
884 | *
885 | * Asserts that a variable is finite.
886 | *
887 | * @param $actual
888 | * @param string $message
889 | * @see \Codeception\Module\AbstractAsserts::assertFinite()
890 | */
891 | public function assertFinite($actual, $message = "") {
892 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertFinite', func_get_args()));
893 | }
894 |
895 |
896 | /**
897 | * [!] Method is generated. Documentation taken from corresponding module.
898 | *
899 | * Asserts that a value is greater than another value.
900 | *
901 | * @param $expected
902 | * @param $actual
903 | * @param string $message
904 | * @see \Codeception\Module\AbstractAsserts::assertGreaterThan()
905 | */
906 | public function assertGreaterThan($expected, $actual, $message = "") {
907 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThan', func_get_args()));
908 | }
909 |
910 |
911 | /**
912 | * [!] Method is generated. Documentation taken from corresponding module.
913 | *
914 | * Asserts that a value is greater than or equal to another value.
915 | *
916 | * @param $expected
917 | * @param $actual
918 | * @param string $message
919 | * @see \Codeception\Module\AbstractAsserts::assertGreaterThanOrEqual()
920 | */
921 | public function assertGreaterThanOrEqual($expected, $actual, $message = "") {
922 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertGreaterThanOrEqual', func_get_args()));
923 | }
924 |
925 |
926 | /**
927 | * [!] Method is generated. Documentation taken from corresponding module.
928 | *
929 | * Asserts that a variable is infinite.
930 | *
931 | * @param $actual
932 | * @param string $message
933 | * @see \Codeception\Module\AbstractAsserts::assertInfinite()
934 | */
935 | public function assertInfinite($actual, $message = "") {
936 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertInfinite', func_get_args()));
937 | }
938 |
939 |
940 | /**
941 | * [!] Method is generated. Documentation taken from corresponding module.
942 | *
943 | * Asserts that a variable is of a given type.
944 | *
945 | * @param $expected
946 | * @param $actual
947 | * @param string $message
948 | * @see \Codeception\Module\AbstractAsserts::assertInstanceOf()
949 | */
950 | public function assertInstanceOf($expected, $actual, $message = "") {
951 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertInstanceOf', func_get_args()));
952 | }
953 |
954 |
955 | /**
956 | * [!] Method is generated. Documentation taken from corresponding module.
957 | *
958 | * Asserts that a variable is of type array.
959 | *
960 | * @param $actual
961 | * @param string $message
962 | * @see \Codeception\Module\AbstractAsserts::assertIsArray()
963 | */
964 | public function assertIsArray($actual, $message = "") {
965 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsArray', func_get_args()));
966 | }
967 |
968 |
969 | /**
970 | * [!] Method is generated. Documentation taken from corresponding module.
971 | *
972 | * Asserts that a variable is of type bool.
973 | *
974 | * @param $actual
975 | * @param string $message
976 | * @see \Codeception\Module\AbstractAsserts::assertIsBool()
977 | */
978 | public function assertIsBool($actual, $message = "") {
979 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsBool', func_get_args()));
980 | }
981 |
982 |
983 | /**
984 | * [!] Method is generated. Documentation taken from corresponding module.
985 | *
986 | * Asserts that a variable is of type callable.
987 | *
988 | * @param $actual
989 | * @param string $message
990 | * @see \Codeception\Module\AbstractAsserts::assertIsCallable()
991 | */
992 | public function assertIsCallable($actual, $message = "") {
993 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsCallable', func_get_args()));
994 | }
995 |
996 |
997 | /**
998 | * [!] Method is generated. Documentation taken from corresponding module.
999 | *
1000 | * Asserts that a variable is of type resource and is closed.
1001 | *
1002 | * @param $actual
1003 | * @param string $message
1004 | * @see \Codeception\Module\AbstractAsserts::assertIsClosedResource()
1005 | */
1006 | public function assertIsClosedResource($actual, $message = "") {
1007 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsClosedResource', func_get_args()));
1008 | }
1009 |
1010 |
1011 | /**
1012 | * [!] Method is generated. Documentation taken from corresponding module.
1013 | *
1014 | * Asserts that a variable is of type float.
1015 | *
1016 | * @param $actual
1017 | * @param string $message
1018 | * @see \Codeception\Module\AbstractAsserts::assertIsFloat()
1019 | */
1020 | public function assertIsFloat($actual, $message = "") {
1021 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsFloat', func_get_args()));
1022 | }
1023 |
1024 |
1025 | /**
1026 | * [!] Method is generated. Documentation taken from corresponding module.
1027 | *
1028 | * Asserts that a variable is of type int.
1029 | *
1030 | * @param $actual
1031 | * @param string $message
1032 | * @see \Codeception\Module\AbstractAsserts::assertIsInt()
1033 | */
1034 | public function assertIsInt($actual, $message = "") {
1035 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsInt', func_get_args()));
1036 | }
1037 |
1038 |
1039 | /**
1040 | * [!] Method is generated. Documentation taken from corresponding module.
1041 | *
1042 | * Asserts that a variable is of type iterable.
1043 | *
1044 | * @param $actual
1045 | * @param string $message
1046 | * @see \Codeception\Module\AbstractAsserts::assertIsIterable()
1047 | */
1048 | public function assertIsIterable($actual, $message = "") {
1049 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsIterable', func_get_args()));
1050 | }
1051 |
1052 |
1053 | /**
1054 | * [!] Method is generated. Documentation taken from corresponding module.
1055 | *
1056 | * Asserts that a variable is not of type array.
1057 | *
1058 | * @param $actual
1059 | * @param string $message
1060 | * @see \Codeception\Module\AbstractAsserts::assertIsNotArray()
1061 | */
1062 | public function assertIsNotArray($actual, $message = "") {
1063 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotArray', func_get_args()));
1064 | }
1065 |
1066 |
1067 | /**
1068 | * [!] Method is generated. Documentation taken from corresponding module.
1069 | *
1070 | * Asserts that a variable is not of type bool.
1071 | *
1072 | * @param $actual
1073 | * @param string $message
1074 | * @see \Codeception\Module\AbstractAsserts::assertIsNotBool()
1075 | */
1076 | public function assertIsNotBool($actual, $message = "") {
1077 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotBool', func_get_args()));
1078 | }
1079 |
1080 |
1081 | /**
1082 | * [!] Method is generated. Documentation taken from corresponding module.
1083 | *
1084 | * Asserts that a variable is not of type callable.
1085 | *
1086 | * @param $actual
1087 | * @param string $message
1088 | * @see \Codeception\Module\AbstractAsserts::assertIsNotCallable()
1089 | */
1090 | public function assertIsNotCallable($actual, $message = "") {
1091 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotCallable', func_get_args()));
1092 | }
1093 |
1094 |
1095 | /**
1096 | * [!] Method is generated. Documentation taken from corresponding module.
1097 | *
1098 | * Asserts that a variable is not of type resource.
1099 | *
1100 | * @param $actual
1101 | * @param string $message
1102 | * @see \Codeception\Module\AbstractAsserts::assertIsNotClosedResource()
1103 | */
1104 | public function assertIsNotClosedResource($actual, $message = "") {
1105 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotClosedResource', func_get_args()));
1106 | }
1107 |
1108 |
1109 | /**
1110 | * [!] Method is generated. Documentation taken from corresponding module.
1111 | *
1112 | * Asserts that a variable is not of type float.
1113 | *
1114 | * @param $actual
1115 | * @param string $message
1116 | * @see \Codeception\Module\AbstractAsserts::assertIsNotFloat()
1117 | */
1118 | public function assertIsNotFloat($actual, $message = "") {
1119 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotFloat', func_get_args()));
1120 | }
1121 |
1122 |
1123 | /**
1124 | * [!] Method is generated. Documentation taken from corresponding module.
1125 | *
1126 | * Asserts that a variable is not of type int.
1127 | *
1128 | * @param $actual
1129 | * @param string $message
1130 | * @see \Codeception\Module\AbstractAsserts::assertIsNotInt()
1131 | */
1132 | public function assertIsNotInt($actual, $message = "") {
1133 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotInt', func_get_args()));
1134 | }
1135 |
1136 |
1137 | /**
1138 | * [!] Method is generated. Documentation taken from corresponding module.
1139 | *
1140 | * Asserts that a variable is not of type iterable.
1141 | *
1142 | * @param $actual
1143 | * @param string $message
1144 | * @see \Codeception\Module\AbstractAsserts::assertIsNotIterable()
1145 | */
1146 | public function assertIsNotIterable($actual, $message = "") {
1147 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotIterable', func_get_args()));
1148 | }
1149 |
1150 |
1151 | /**
1152 | * [!] Method is generated. Documentation taken from corresponding module.
1153 | *
1154 | * Asserts that a variable is not of type numeric.
1155 | *
1156 | * @param $actual
1157 | * @param string $message
1158 | * @see \Codeception\Module\AbstractAsserts::assertIsNotNumeric()
1159 | */
1160 | public function assertIsNotNumeric($actual, $message = "") {
1161 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotNumeric', func_get_args()));
1162 | }
1163 |
1164 |
1165 | /**
1166 | * [!] Method is generated. Documentation taken from corresponding module.
1167 | *
1168 | * Asserts that a variable is not of type object.
1169 | *
1170 | * @param $actual
1171 | * @param string $message
1172 | * @see \Codeception\Module\AbstractAsserts::assertIsNotObject()
1173 | */
1174 | public function assertIsNotObject($actual, $message = "") {
1175 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotObject', func_get_args()));
1176 | }
1177 |
1178 |
1179 | /**
1180 | * [!] Method is generated. Documentation taken from corresponding module.
1181 | *
1182 | * Asserts that a file/dir exists and is not readable.
1183 | *
1184 | * @param string $filename
1185 | * @param string $message
1186 | * @see \Codeception\Module\AbstractAsserts::assertIsNotReadable()
1187 | */
1188 | public function assertIsNotReadable($filename, $message = "") {
1189 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotReadable', func_get_args()));
1190 | }
1191 |
1192 |
1193 | /**
1194 | * [!] Method is generated. Documentation taken from corresponding module.
1195 | *
1196 | * Asserts that a variable is not of type resource.
1197 | *
1198 | * @param $actual
1199 | * @param string $message
1200 | * @see \Codeception\Module\AbstractAsserts::assertIsNotResource()
1201 | */
1202 | public function assertIsNotResource($actual, $message = "") {
1203 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotResource', func_get_args()));
1204 | }
1205 |
1206 |
1207 | /**
1208 | * [!] Method is generated. Documentation taken from corresponding module.
1209 | *
1210 | * Asserts that a variable is not of type scalar.
1211 | *
1212 | * @param $actual
1213 | * @param string $message
1214 | * @see \Codeception\Module\AbstractAsserts::assertIsNotScalar()
1215 | */
1216 | public function assertIsNotScalar($actual, $message = "") {
1217 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotScalar', func_get_args()));
1218 | }
1219 |
1220 |
1221 | /**
1222 | * [!] Method is generated. Documentation taken from corresponding module.
1223 | *
1224 | * Asserts that a variable is not of type string.
1225 | *
1226 | * @param $actual
1227 | * @param string $message
1228 | * @see \Codeception\Module\AbstractAsserts::assertIsNotString()
1229 | */
1230 | public function assertIsNotString($actual, $message = "") {
1231 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotString', func_get_args()));
1232 | }
1233 |
1234 |
1235 | /**
1236 | * [!] Method is generated. Documentation taken from corresponding module.
1237 | *
1238 | * Asserts that a file/dir exists and is not writable.
1239 | *
1240 | * @param $filename
1241 | * @param string $message
1242 | * @see \Codeception\Module\AbstractAsserts::assertIsNotWritable()
1243 | */
1244 | public function assertIsNotWritable($filename, $message = "") {
1245 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNotWritable', func_get_args()));
1246 | }
1247 |
1248 |
1249 | /**
1250 | * [!] Method is generated. Documentation taken from corresponding module.
1251 | *
1252 | * Asserts that a variable is of type numeric.
1253 | *
1254 | * @param $actual
1255 | * @param string $message
1256 | * @see \Codeception\Module\AbstractAsserts::assertIsNumeric()
1257 | */
1258 | public function assertIsNumeric($actual, $message = "") {
1259 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsNumeric', func_get_args()));
1260 | }
1261 |
1262 |
1263 | /**
1264 | * [!] Method is generated. Documentation taken from corresponding module.
1265 | *
1266 | * Asserts that a variable is of type object.
1267 | *
1268 | * @param $actual
1269 | * @param string $message
1270 | * @see \Codeception\Module\AbstractAsserts::assertIsObject()
1271 | */
1272 | public function assertIsObject($actual, $message = "") {
1273 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsObject', func_get_args()));
1274 | }
1275 |
1276 |
1277 | /**
1278 | * [!] Method is generated. Documentation taken from corresponding module.
1279 | *
1280 | * Asserts that a file/dir is readable.
1281 | *
1282 | * @param $filename
1283 | * @param string $message
1284 | * @see \Codeception\Module\AbstractAsserts::assertIsReadable()
1285 | */
1286 | public function assertIsReadable($filename, $message = "") {
1287 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsReadable', func_get_args()));
1288 | }
1289 |
1290 |
1291 | /**
1292 | * [!] Method is generated. Documentation taken from corresponding module.
1293 | *
1294 | * Asserts that a variable is of type resource.
1295 | *
1296 | * @param $actual
1297 | * @param string $message
1298 | * @see \Codeception\Module\AbstractAsserts::assertIsResource()
1299 | */
1300 | public function assertIsResource($actual, $message = "") {
1301 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsResource', func_get_args()));
1302 | }
1303 |
1304 |
1305 | /**
1306 | * [!] Method is generated. Documentation taken from corresponding module.
1307 | *
1308 | * Asserts that a variable is of type scalar.
1309 | *
1310 | * @param $actual
1311 | * @param string $message
1312 | * @see \Codeception\Module\AbstractAsserts::assertIsScalar()
1313 | */
1314 | public function assertIsScalar($actual, $message = "") {
1315 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsScalar', func_get_args()));
1316 | }
1317 |
1318 |
1319 | /**
1320 | * [!] Method is generated. Documentation taken from corresponding module.
1321 | *
1322 | * Asserts that a variable is of type string.
1323 | *
1324 | * @param $actual
1325 | * @param string $message
1326 | * @see \Codeception\Module\AbstractAsserts::assertIsString()
1327 | */
1328 | public function assertIsString($actual, $message = "") {
1329 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsString', func_get_args()));
1330 | }
1331 |
1332 |
1333 | /**
1334 | * [!] Method is generated. Documentation taken from corresponding module.
1335 | *
1336 | * Asserts that a file/dir exists and is writable.
1337 | *
1338 | * @param $filename
1339 | * @param string $message
1340 | * @see \Codeception\Module\AbstractAsserts::assertIsWritable()
1341 | */
1342 | public function assertIsWritable($filename, $message = "") {
1343 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertIsWritable', func_get_args()));
1344 | }
1345 |
1346 |
1347 | /**
1348 | * [!] Method is generated. Documentation taken from corresponding module.
1349 | *
1350 | * Asserts that a string is a valid JSON string.
1351 | *
1352 | * @param string $actualJson
1353 | * @param string $message
1354 | * @see \Codeception\Module\AbstractAsserts::assertJson()
1355 | */
1356 | public function assertJson($actualJson, $message = "") {
1357 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJson', func_get_args()));
1358 | }
1359 |
1360 |
1361 | /**
1362 | * [!] Method is generated. Documentation taken from corresponding module.
1363 | *
1364 | * Asserts that two JSON files are equal.
1365 | *
1366 | * @param string $expectedFile
1367 | * @param string $actualFile
1368 | * @param string $message
1369 | * @see \Codeception\Module\AbstractAsserts::assertJsonFileEqualsJsonFile()
1370 | */
1371 | public function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = "") {
1372 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJsonFileEqualsJsonFile', func_get_args()));
1373 | }
1374 |
1375 |
1376 | /**
1377 | * [!] Method is generated. Documentation taken from corresponding module.
1378 | *
1379 | * Asserts that two JSON files are not equal.
1380 | *
1381 | * @param string $expectedFile
1382 | * @param string $actualFile
1383 | * @param string $message
1384 | * @see \Codeception\Module\AbstractAsserts::assertJsonFileNotEqualsJsonFile()
1385 | */
1386 | public function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = "") {
1387 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJsonFileNotEqualsJsonFile', func_get_args()));
1388 | }
1389 |
1390 |
1391 | /**
1392 | * [!] Method is generated. Documentation taken from corresponding module.
1393 | *
1394 | * Asserts that the generated JSON encoded object and the content of the given file are equal.
1395 | *
1396 | * @param string $expectedFile
1397 | * @param string $actualJson
1398 | * @param string $message
1399 | * @see \Codeception\Module\AbstractAsserts::assertJsonStringEqualsJsonFile()
1400 | */
1401 | public function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = "") {
1402 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJsonStringEqualsJsonFile', func_get_args()));
1403 | }
1404 |
1405 |
1406 | /**
1407 | * [!] Method is generated. Documentation taken from corresponding module.
1408 | *
1409 | * Asserts that two given JSON encoded objects or arrays are equal.
1410 | *
1411 | * @param string $expectedJson
1412 | * @param string $actualJson
1413 | * @param string $message
1414 | * @see \Codeception\Module\AbstractAsserts::assertJsonStringEqualsJsonString()
1415 | */
1416 | public function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = "") {
1417 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJsonStringEqualsJsonString', func_get_args()));
1418 | }
1419 |
1420 |
1421 | /**
1422 | * [!] Method is generated. Documentation taken from corresponding module.
1423 | *
1424 | * Asserts that the generated JSON encoded object and the content of the given file are not equal.
1425 | *
1426 | * @param string $expectedFile
1427 | * @param string $actualJson
1428 | * @param string $message
1429 | * @see \Codeception\Module\AbstractAsserts::assertJsonStringNotEqualsJsonFile()
1430 | */
1431 | public function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = "") {
1432 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJsonStringNotEqualsJsonFile', func_get_args()));
1433 | }
1434 |
1435 |
1436 | /**
1437 | * [!] Method is generated. Documentation taken from corresponding module.
1438 | *
1439 | * Asserts that two given JSON encoded objects or arrays are not equal.
1440 | *
1441 | * @param string $expectedJson
1442 | * @param string $actualJson
1443 | * @param string $message
1444 | * @see \Codeception\Module\AbstractAsserts::assertJsonStringNotEqualsJsonString()
1445 | */
1446 | public function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = "") {
1447 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertJsonStringNotEqualsJsonString', func_get_args()));
1448 | }
1449 |
1450 |
1451 | /**
1452 | * [!] Method is generated. Documentation taken from corresponding module.
1453 | *
1454 | * Asserts that a value is smaller than another value.
1455 | *
1456 | * @param $expected
1457 | * @param $actual
1458 | * @param string $message
1459 | * @see \Codeception\Module\AbstractAsserts::assertLessThan()
1460 | */
1461 | public function assertLessThan($expected, $actual, $message = "") {
1462 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThan', func_get_args()));
1463 | }
1464 |
1465 |
1466 | /**
1467 | * [!] Method is generated. Documentation taken from corresponding module.
1468 | *
1469 | * Asserts that a value is smaller than or equal to another value.
1470 | *
1471 | * @param $expected
1472 | * @param $actual
1473 | * @param string $message
1474 | * @see \Codeception\Module\AbstractAsserts::assertLessThanOrEqual()
1475 | */
1476 | public function assertLessThanOrEqual($expected, $actual, $message = "") {
1477 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertLessThanOrEqual', func_get_args()));
1478 | }
1479 |
1480 |
1481 | /**
1482 | * [!] Method is generated. Documentation taken from corresponding module.
1483 | *
1484 | * Asserts that a string matches a given regular expression.
1485 | *
1486 | * @param string $pattern
1487 | * @param string $string
1488 | * @param string $message
1489 | * @see \Codeception\Module\AbstractAsserts::assertMatchesRegularExpression()
1490 | */
1491 | public function assertMatchesRegularExpression($pattern, $string, $message = "") {
1492 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertMatchesRegularExpression', func_get_args()));
1493 | }
1494 |
1495 |
1496 | /**
1497 | * [!] Method is generated. Documentation taken from corresponding module.
1498 | *
1499 | * Asserts that a variable is nan.
1500 | *
1501 | * @param $actual
1502 | * @param string $message
1503 | * @see \Codeception\Module\AbstractAsserts::assertNan()
1504 | */
1505 | public function assertNan($actual, $message = "") {
1506 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNan', func_get_args()));
1507 | }
1508 |
1509 |
1510 | /**
1511 | * [!] Method is generated. Documentation taken from corresponding module.
1512 | *
1513 | * Asserts that a haystack does not contain a needle.
1514 | *
1515 | * @param $needle
1516 | * @param $haystack
1517 | * @param string $message
1518 | * @see \Codeception\Module\AbstractAsserts::assertNotContains()
1519 | */
1520 | public function assertNotContains($needle, $haystack, $message = "") {
1521 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContains', func_get_args()));
1522 | }
1523 |
1524 |
1525 | /**
1526 | * [!] Method is generated. Documentation taken from corresponding module.
1527 | *
1528 | *
1529 | * @see \Codeception\Module\AbstractAsserts::assertNotContainsEquals()
1530 | */
1531 | public function assertNotContainsEquals($needle, $haystack, $message = "") {
1532 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContainsEquals', func_get_args()));
1533 | }
1534 |
1535 |
1536 | /**
1537 | * [!] Method is generated. Documentation taken from corresponding module.
1538 | *
1539 | * Asserts that a haystack does not contain only values of a given type.
1540 | *
1541 | * @param string $type
1542 | * @param $haystack
1543 | * @param bool|null $isNativeType
1544 | * @param string $message
1545 | * @see \Codeception\Module\AbstractAsserts::assertNotContainsOnly()
1546 | */
1547 | public function assertNotContainsOnly($type, $haystack, $isNativeType = NULL, $message = "") {
1548 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContainsOnly', func_get_args()));
1549 | }
1550 |
1551 |
1552 | /**
1553 | * [!] Method is generated. Documentation taken from corresponding module.
1554 | *
1555 | * Asserts the number of elements of an array, Countable or Traversable.
1556 | *
1557 | * @param int $expectedCount
1558 | * @param Countable|iterable $haystack
1559 | * @param string $message
1560 | * @see \Codeception\Module\AbstractAsserts::assertNotCount()
1561 | */
1562 | public function assertNotCount($expectedCount, $haystack, $message = "") {
1563 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotCount', func_get_args()));
1564 | }
1565 |
1566 |
1567 | /**
1568 | * [!] Method is generated. Documentation taken from corresponding module.
1569 | *
1570 | * Asserts that a variable is not empty.
1571 | *
1572 | * @param $actual
1573 | * @param string $message
1574 | * @see \Codeception\Module\AbstractAsserts::assertNotEmpty()
1575 | */
1576 | public function assertNotEmpty($actual, $message = "") {
1577 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEmpty', func_get_args()));
1578 | }
1579 |
1580 |
1581 | /**
1582 | * [!] Method is generated. Documentation taken from corresponding module.
1583 | *
1584 | * Asserts that two variables are not equal.
1585 | *
1586 | * @param $expected
1587 | * @param $actual
1588 | * @param string $message
1589 | * @see \Codeception\Module\AbstractAsserts::assertNotEquals()
1590 | */
1591 | public function assertNotEquals($expected, $actual, $message = "") {
1592 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEquals', func_get_args()));
1593 | }
1594 |
1595 |
1596 | /**
1597 | * [!] Method is generated. Documentation taken from corresponding module.
1598 | *
1599 | * Asserts that two variables are not equal (canonicalizing).
1600 | *
1601 | * @param $expected
1602 | * @param $actual
1603 | * @param string $message
1604 | * @see \Codeception\Module\AbstractAsserts::assertNotEqualsCanonicalizing()
1605 | */
1606 | public function assertNotEqualsCanonicalizing($expected, $actual, $message = "") {
1607 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEqualsCanonicalizing', func_get_args()));
1608 | }
1609 |
1610 |
1611 | /**
1612 | * [!] Method is generated. Documentation taken from corresponding module.
1613 | *
1614 | * Asserts that two variables are not equal (ignoring case).
1615 | *
1616 | * @param $expected
1617 | * @param $actual
1618 | * @param string $message
1619 | * @see \Codeception\Module\AbstractAsserts::assertNotEqualsIgnoringCase()
1620 | */
1621 | public function assertNotEqualsIgnoringCase($expected, $actual, $message = "") {
1622 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEqualsIgnoringCase', func_get_args()));
1623 | }
1624 |
1625 |
1626 | /**
1627 | * [!] Method is generated. Documentation taken from corresponding module.
1628 | *
1629 | * Asserts that two variables are not equal (with delta).
1630 | *
1631 | * @param $expected
1632 | * @param $actual
1633 | * @param float $delta
1634 | * @param string $message
1635 | * @see \Codeception\Module\AbstractAsserts::assertNotEqualsWithDelta()
1636 | */
1637 | public function assertNotEqualsWithDelta($expected, $actual, $delta, $message = "") {
1638 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotEqualsWithDelta', func_get_args()));
1639 | }
1640 |
1641 |
1642 | /**
1643 | * [!] Method is generated. Documentation taken from corresponding module.
1644 | *
1645 | * Asserts that a condition is not false.
1646 | *
1647 | * @param $condition
1648 | * @param string $message
1649 | * @see \Codeception\Module\AbstractAsserts::assertNotFalse()
1650 | */
1651 | public function assertNotFalse($condition, $message = "") {
1652 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotFalse', func_get_args()));
1653 | }
1654 |
1655 |
1656 | /**
1657 | * [!] Method is generated. Documentation taken from corresponding module.
1658 | *
1659 | * Asserts that a variable is not of a given type.
1660 | *
1661 | * @param $expected
1662 | * @param $actual
1663 | * @param string $message
1664 | * @see \Codeception\Module\AbstractAsserts::assertNotInstanceOf()
1665 | */
1666 | public function assertNotInstanceOf($expected, $actual, $message = "") {
1667 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotInstanceOf', func_get_args()));
1668 | }
1669 |
1670 |
1671 | /**
1672 | * [!] Method is generated. Documentation taken from corresponding module.
1673 | *
1674 | * Asserts that a variable is not null.
1675 | *
1676 | * @param $actual
1677 | * @param string $message
1678 | * @see \Codeception\Module\AbstractAsserts::assertNotNull()
1679 | */
1680 | public function assertNotNull($actual, $message = "") {
1681 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotNull', func_get_args()));
1682 | }
1683 |
1684 |
1685 | /**
1686 | * [!] Method is generated. Documentation taken from corresponding module.
1687 | *
1688 | * Asserts that two variables do not have the same type and value.
1689 | *
1690 | * @param $expected
1691 | * @param $actual
1692 | * @param string $message
1693 | * @see \Codeception\Module\AbstractAsserts::assertNotSame()
1694 | */
1695 | public function assertNotSame($expected, $actual, $message = "") {
1696 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotSame', func_get_args()));
1697 | }
1698 |
1699 |
1700 | /**
1701 | * [!] Method is generated. Documentation taken from corresponding module.
1702 | *
1703 | * Assert that the size of two arrays (or `Countable` or `Traversable` objects) is not the same.
1704 | *
1705 | * @param Countable|iterable $expected
1706 | * @param Countable|iterable $actual
1707 | * @param string $message
1708 | * @see \Codeception\Module\AbstractAsserts::assertNotSameSize()
1709 | */
1710 | public function assertNotSameSize($expected, $actual, $message = "") {
1711 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotSameSize', func_get_args()));
1712 | }
1713 |
1714 |
1715 | /**
1716 | * [!] Method is generated. Documentation taken from corresponding module.
1717 | *
1718 | * Asserts that a condition is not true.
1719 | *
1720 | * @param $condition
1721 | * @param string $message
1722 | * @see \Codeception\Module\AbstractAsserts::assertNotTrue()
1723 | */
1724 | public function assertNotTrue($condition, $message = "") {
1725 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotTrue', func_get_args()));
1726 | }
1727 |
1728 |
1729 | /**
1730 | * [!] Method is generated. Documentation taken from corresponding module.
1731 | *
1732 | * Asserts that a variable is null.
1733 | *
1734 | * @param $actual
1735 | * @param string $message
1736 | * @see \Codeception\Module\AbstractAsserts::assertNull()
1737 | */
1738 | public function assertNull($actual, $message = "") {
1739 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNull', func_get_args()));
1740 | }
1741 |
1742 |
1743 | /**
1744 | * [!] Method is generated. Documentation taken from corresponding module.
1745 | *
1746 | * Asserts that an object has a specified attribute.
1747 | *
1748 | * @param string $attributeName
1749 | * @param object $object
1750 | * @param string $message
1751 | * @see \Codeception\Module\AbstractAsserts::assertObjectHasAttribute()
1752 | */
1753 | public function assertObjectHasAttribute($attributeName, $object, $message = "") {
1754 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertObjectHasAttribute', func_get_args()));
1755 | }
1756 |
1757 |
1758 | /**
1759 | * [!] Method is generated. Documentation taken from corresponding module.
1760 | *
1761 | * Asserts that an object does not have a specified attribute.
1762 | *
1763 | * @param string $attributeName
1764 | * @param object $object
1765 | * @param string $message
1766 | * @see \Codeception\Module\AbstractAsserts::assertObjectNotHasAttribute()
1767 | */
1768 | public function assertObjectNotHasAttribute($attributeName, $object, $message = "") {
1769 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertObjectNotHasAttribute', func_get_args()));
1770 | }
1771 |
1772 |
1773 | /**
1774 | * [!] Method is generated. Documentation taken from corresponding module.
1775 | *
1776 | * Asserts that two variables have the same type and value.
1777 | *
1778 | * @param $expected
1779 | * @param $actual
1780 | * @param string $message
1781 | * @see \Codeception\Module\AbstractAsserts::assertSame()
1782 | */
1783 | public function assertSame($expected, $actual, $message = "") {
1784 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertSame', func_get_args()));
1785 | }
1786 |
1787 |
1788 | /**
1789 | * [!] Method is generated. Documentation taken from corresponding module.
1790 | *
1791 | * Assert that the size of two arrays (or `Countable` or `Traversable` objects) is the same.
1792 | *
1793 | * @param Countable|iterable $expected
1794 | * @param Countable|iterable $actual
1795 | * @param string $message
1796 | * @see \Codeception\Module\AbstractAsserts::assertSameSize()
1797 | */
1798 | public function assertSameSize($expected, $actual, $message = "") {
1799 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertSameSize', func_get_args()));
1800 | }
1801 |
1802 |
1803 | /**
1804 | * [!] Method is generated. Documentation taken from corresponding module.
1805 | *
1806 | * @param string $needle
1807 | * @param string $haystack
1808 | * @param string $message
1809 | * @see \Codeception\Module\AbstractAsserts::assertStringContainsString()
1810 | */
1811 | public function assertStringContainsString($needle, $haystack, $message = "") {
1812 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringContainsString', func_get_args()));
1813 | }
1814 |
1815 |
1816 | /**
1817 | * [!] Method is generated. Documentation taken from corresponding module.
1818 | *
1819 | *
1820 | * @see \Codeception\Module\AbstractAsserts::assertStringContainsStringIgnoringCase()
1821 | */
1822 | public function assertStringContainsStringIgnoringCase($needle, $haystack, $message = "") {
1823 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringContainsStringIgnoringCase', func_get_args()));
1824 | }
1825 |
1826 |
1827 | /**
1828 | * [!] Method is generated. Documentation taken from corresponding module.
1829 | *
1830 | * Asserts that a string ends not with a given suffix.
1831 | *
1832 | * @param string $suffix
1833 | * @param string $string
1834 | * @param string $message
1835 | * @see \Codeception\Module\AbstractAsserts::assertStringEndsNotWith()
1836 | */
1837 | public function assertStringEndsNotWith($suffix, $string, $message = "") {
1838 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringEndsNotWith', func_get_args()));
1839 | }
1840 |
1841 |
1842 | /**
1843 | * [!] Method is generated. Documentation taken from corresponding module.
1844 | *
1845 | * Asserts that a string ends with a given suffix.
1846 | *
1847 | * @param string $suffix
1848 | * @param string $string
1849 | * @param string $message
1850 | * @see \Codeception\Module\AbstractAsserts::assertStringEndsWith()
1851 | */
1852 | public function assertStringEndsWith($suffix, $string, $message = "") {
1853 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringEndsWith', func_get_args()));
1854 | }
1855 |
1856 |
1857 | /**
1858 | * [!] Method is generated. Documentation taken from corresponding module.
1859 | *
1860 | * Asserts that the contents of a string is equal to the contents of a file.
1861 | *
1862 | * @param string $expectedFile
1863 | * @param string $actualString
1864 | * @param string $message
1865 | * @see \Codeception\Module\AbstractAsserts::assertStringEqualsFile()
1866 | */
1867 | public function assertStringEqualsFile($expectedFile, $actualString, $message = "") {
1868 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringEqualsFile', func_get_args()));
1869 | }
1870 |
1871 |
1872 | /**
1873 | * [!] Method is generated. Documentation taken from corresponding module.
1874 | *
1875 | * Asserts that the contents of a string is equal to the contents of a file (canonicalizing).
1876 | *
1877 | * @param string $expectedFile
1878 | * @param string $actualString
1879 | * @param string $message
1880 | * @see \Codeception\Module\AbstractAsserts::assertStringEqualsFileCanonicalizing()
1881 | */
1882 | public function assertStringEqualsFileCanonicalizing($expectedFile, $actualString, $message = "") {
1883 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringEqualsFileCanonicalizing', func_get_args()));
1884 | }
1885 |
1886 |
1887 | /**
1888 | * [!] Method is generated. Documentation taken from corresponding module.
1889 | *
1890 | * Asserts that the contents of a string is equal to the contents of a file (ignoring case).
1891 | *
1892 | * @param string $expectedFile
1893 | * @param string $actualString
1894 | * @param string $message
1895 | * @see \Codeception\Module\AbstractAsserts::assertStringEqualsFileIgnoringCase()
1896 | */
1897 | public function assertStringEqualsFileIgnoringCase($expectedFile, $actualString, $message = "") {
1898 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringEqualsFileIgnoringCase', func_get_args()));
1899 | }
1900 |
1901 |
1902 | /**
1903 | * [!] Method is generated. Documentation taken from corresponding module.
1904 | *
1905 | * Asserts that a string matches a given format string.
1906 | *
1907 | * @param string $format
1908 | * @param string $string
1909 | * @param string $message
1910 | * @see \Codeception\Module\AbstractAsserts::assertStringMatchesFormat()
1911 | */
1912 | public function assertStringMatchesFormat($format, $string, $message = "") {
1913 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringMatchesFormat', func_get_args()));
1914 | }
1915 |
1916 |
1917 | /**
1918 | * [!] Method is generated. Documentation taken from corresponding module.
1919 | *
1920 | * Asserts that a string matches a given format file.
1921 | *
1922 | * @param string $formatFile
1923 | * @param string $string
1924 | * @param string $message
1925 | * @see \Codeception\Module\AbstractAsserts::assertStringMatchesFormatFile()
1926 | */
1927 | public function assertStringMatchesFormatFile($formatFile, $string, $message = "") {
1928 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringMatchesFormatFile', func_get_args()));
1929 | }
1930 |
1931 |
1932 | /**
1933 | * [!] Method is generated. Documentation taken from corresponding module.
1934 | *
1935 | * @param string $needle
1936 | * @param string $haystack
1937 | * @param string $message
1938 | * @see \Codeception\Module\AbstractAsserts::assertStringNotContainsString()
1939 | */
1940 | public function assertStringNotContainsString($needle, $haystack, $message = "") {
1941 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotContainsString', func_get_args()));
1942 | }
1943 |
1944 |
1945 | /**
1946 | * [!] Method is generated. Documentation taken from corresponding module.
1947 | *
1948 | * @param string $needle
1949 | * @param string $haystack
1950 | * @param string $message
1951 | * @see \Codeception\Module\AbstractAsserts::assertStringNotContainsStringIgnoringCase()
1952 | */
1953 | public function assertStringNotContainsStringIgnoringCase($needle, $haystack, $message = "") {
1954 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotContainsStringIgnoringCase', func_get_args()));
1955 | }
1956 |
1957 |
1958 | /**
1959 | * [!] Method is generated. Documentation taken from corresponding module.
1960 | *
1961 | * Asserts that the contents of a string is not equal to the contents of a file.
1962 | *
1963 | * @param string $expectedFile
1964 | * @param string $actualString
1965 | * @param string $message
1966 | * @see \Codeception\Module\AbstractAsserts::assertStringNotEqualsFile()
1967 | */
1968 | public function assertStringNotEqualsFile($expectedFile, $actualString, $message = "") {
1969 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotEqualsFile', func_get_args()));
1970 | }
1971 |
1972 |
1973 | /**
1974 | * [!] Method is generated. Documentation taken from corresponding module.
1975 | *
1976 | * Asserts that the contents of a string is not equal to the contents of a file (canonicalizing).
1977 | * @param string $expectedFile
1978 | * @param string $actualString
1979 | * @param string $message
1980 | * @see \Codeception\Module\AbstractAsserts::assertStringNotEqualsFileCanonicalizing()
1981 | */
1982 | public function assertStringNotEqualsFileCanonicalizing($expectedFile, $actualString, $message = "") {
1983 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotEqualsFileCanonicalizing', func_get_args()));
1984 | }
1985 |
1986 |
1987 | /**
1988 | * [!] Method is generated. Documentation taken from corresponding module.
1989 | *
1990 | * Asserts that the contents of a string is not equal to the contents of a file (ignoring case).
1991 | *
1992 | * @param string $expectedFile
1993 | * @param string $actualString
1994 | * @param string $message
1995 | * @see \Codeception\Module\AbstractAsserts::assertStringNotEqualsFileIgnoringCase()
1996 | */
1997 | public function assertStringNotEqualsFileIgnoringCase($expectedFile, $actualString, $message = "") {
1998 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotEqualsFileIgnoringCase', func_get_args()));
1999 | }
2000 |
2001 |
2002 | /**
2003 | * [!] Method is generated. Documentation taken from corresponding module.
2004 | *
2005 | * Asserts that a string does not match a given format string.
2006 | *
2007 | * @param string $format
2008 | * @param string $string
2009 | * @param string $message
2010 | * @see \Codeception\Module\AbstractAsserts::assertStringNotMatchesFormat()
2011 | */
2012 | public function assertStringNotMatchesFormat($format, $string, $message = "") {
2013 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotMatchesFormat', func_get_args()));
2014 | }
2015 |
2016 |
2017 | /**
2018 | * [!] Method is generated. Documentation taken from corresponding module.
2019 | *
2020 | * Asserts that a string does not match a given format string.
2021 | *
2022 | * @param string $formatFile
2023 | * @param string $string
2024 | * @param string $message
2025 | * @see \Codeception\Module\AbstractAsserts::assertStringNotMatchesFormatFile()
2026 | */
2027 | public function assertStringNotMatchesFormatFile($formatFile, $string, $message = "") {
2028 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringNotMatchesFormatFile', func_get_args()));
2029 | }
2030 |
2031 |
2032 | /**
2033 | * [!] Method is generated. Documentation taken from corresponding module.
2034 | *
2035 | * Asserts that a string starts not with a given prefix.
2036 | *
2037 | * @param string $prefix
2038 | * @param string $string
2039 | * @param string $message
2040 | * @see \Codeception\Module\AbstractAsserts::assertStringStartsNotWith()
2041 | */
2042 | public function assertStringStartsNotWith($prefix, $string, $message = "") {
2043 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringStartsNotWith', func_get_args()));
2044 | }
2045 |
2046 |
2047 | /**
2048 | * [!] Method is generated. Documentation taken from corresponding module.
2049 | *
2050 | * Asserts that a string starts with a given prefix.
2051 | *
2052 | * @param string $prefix
2053 | * @param string $string
2054 | * @param string $message
2055 | * @see \Codeception\Module\AbstractAsserts::assertStringStartsWith()
2056 | */
2057 | public function assertStringStartsWith($prefix, $string, $message = "") {
2058 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertStringStartsWith', func_get_args()));
2059 | }
2060 |
2061 |
2062 | /**
2063 | * [!] Method is generated. Documentation taken from corresponding module.
2064 | *
2065 | * Evaluates a PHPUnit\Framework\Constraint matcher object.
2066 | *
2067 | * @param $value
2068 | * @param Constraint $constraint
2069 | * @param string $message
2070 | * @see \Codeception\Module\AbstractAsserts::assertThat()
2071 | */
2072 | public function assertThat($value, $constraint, $message = "") {
2073 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertThat', func_get_args()));
2074 | }
2075 |
2076 |
2077 | /**
2078 | * [!] Method is generated. Documentation taken from corresponding module.
2079 | *
2080 | * Asserts that a condition is true.
2081 | *
2082 | * @param $condition
2083 | * @param string $message
2084 | * @see \Codeception\Module\AbstractAsserts::assertTrue()
2085 | */
2086 | public function assertTrue($condition, $message = "") {
2087 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertTrue', func_get_args()));
2088 | }
2089 |
2090 |
2091 | /**
2092 | * [!] Method is generated. Documentation taken from corresponding module.
2093 | *
2094 | * Asserts that two XML files are equal.
2095 | *
2096 | * @param string $expectedFile
2097 | * @param string $actualFile
2098 | * @param string $message
2099 | * @see \Codeception\Module\AbstractAsserts::assertXmlFileEqualsXmlFile()
2100 | */
2101 | public function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = "") {
2102 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertXmlFileEqualsXmlFile', func_get_args()));
2103 | }
2104 |
2105 |
2106 | /**
2107 | * [!] Method is generated. Documentation taken from corresponding module.
2108 | *
2109 | * Asserts that two XML files are not equal.
2110 | *
2111 | * @param string $expectedFile
2112 | * @param string $actualFile
2113 | * @param string $message
2114 | * @see \Codeception\Module\AbstractAsserts::assertXmlFileNotEqualsXmlFile()
2115 | */
2116 | public function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = "") {
2117 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertXmlFileNotEqualsXmlFile', func_get_args()));
2118 | }
2119 |
2120 |
2121 | /**
2122 | * [!] Method is generated. Documentation taken from corresponding module.
2123 | *
2124 | * Asserts that two XML documents are equal.
2125 | *
2126 | * @param string $expectedFile
2127 | * @param DOMDocument|string $actualXml
2128 | * @param string $message
2129 | * @see \Codeception\Module\AbstractAsserts::assertXmlStringEqualsXmlFile()
2130 | */
2131 | public function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = "") {
2132 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertXmlStringEqualsXmlFile', func_get_args()));
2133 | }
2134 |
2135 |
2136 | /**
2137 | * [!] Method is generated. Documentation taken from corresponding module.
2138 | *
2139 | * Asserts that two XML documents are equal.
2140 | *
2141 | * @param DOMDocument|string $expectedXml
2142 | * @param DOMDocument|string $actualXml
2143 | * @param string $message
2144 | * @see \Codeception\Module\AbstractAsserts::assertXmlStringEqualsXmlString()
2145 | */
2146 | public function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = "") {
2147 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertXmlStringEqualsXmlString', func_get_args()));
2148 | }
2149 |
2150 |
2151 | /**
2152 | * [!] Method is generated. Documentation taken from corresponding module.
2153 | *
2154 | * Asserts that two XML documents are not equal.
2155 | *
2156 | * @param string $expectedFile
2157 | * @param DOMDocument|string $actualXml
2158 | * @param string $message
2159 | * @see \Codeception\Module\AbstractAsserts::assertXmlStringNotEqualsXmlFile()
2160 | */
2161 | public function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = "") {
2162 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertXmlStringNotEqualsXmlFile', func_get_args()));
2163 | }
2164 |
2165 |
2166 | /**
2167 | * [!] Method is generated. Documentation taken from corresponding module.
2168 | *
2169 | * Asserts that two XML documents are not equal.
2170 | *
2171 | * @param DOMDocument|string $expectedXml
2172 | * @param DOMDocument|string $actualXml
2173 | * @param string $message
2174 | * @see \Codeception\Module\AbstractAsserts::assertXmlStringNotEqualsXmlString()
2175 | */
2176 | public function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = "") {
2177 | return $this->getScenario()->runStep(new \Codeception\Step\Action('assertXmlStringNotEqualsXmlString', func_get_args()));
2178 | }
2179 |
2180 |
2181 | /**
2182 | * [!] Method is generated. Documentation taken from corresponding module.
2183 | *
2184 | * Fails a test with the given message.
2185 | *
2186 | * @param string $message
2187 | * @see \Codeception\Module\AbstractAsserts::fail()
2188 | */
2189 | public function fail($message = "") {
2190 | return $this->getScenario()->runStep(new \Codeception\Step\Action('fail', func_get_args()));
2191 | }
2192 |
2193 |
2194 | /**
2195 | * [!] Method is generated. Documentation taken from corresponding module.
2196 | *
2197 | * Mark the test as incomplete.
2198 | *
2199 | * @param string $message
2200 | * @see \Codeception\Module\AbstractAsserts::markTestIncomplete()
2201 | */
2202 | public function markTestIncomplete($message = "") {
2203 | return $this->getScenario()->runStep(new \Codeception\Step\Action('markTestIncomplete', func_get_args()));
2204 | }
2205 |
2206 |
2207 | /**
2208 | * [!] Method is generated. Documentation taken from corresponding module.
2209 | *
2210 | * Mark the test as skipped.
2211 | *
2212 | * @param string $message
2213 | * @see \Codeception\Module\AbstractAsserts::markTestSkipped()
2214 | */
2215 | public function markTestSkipped($message = "") {
2216 | return $this->getScenario()->runStep(new \Codeception\Step\Action('markTestSkipped', func_get_args()));
2217 | }
2218 | }
2219 |
--------------------------------------------------------------------------------
/tests/config.php:
--------------------------------------------------------------------------------
1 | 'wpn-tests',
7 | 'basePath' => dirname(__DIR__),
8 | 'bootstrap' => ['wpn'],
9 | 'components' => [
10 | 'db' => [
11 | 'class' => Connection::class,
12 | 'dsn' => 'mysql:host=127.0.0.1;dbname=wpn_test_db;',
13 | 'username' => 'root',
14 | 'password' => 'password',
15 | 'enableSchemaCache' => true,
16 | 'charset' => 'latin1',
17 | ],
18 | ],
19 | 'modules' => [
20 | 'wpn' => [
21 | 'class' => \machour\yii2\wpn\Module::class,
22 | 'components' => [
23 | 'pusher' => [
24 | 'class' => \machour\yii2\wpn\components\Pusher::class,
25 | ]
26 | ]
27 | ]
28 | ]
29 | ];
--------------------------------------------------------------------------------
/tests/fixtures/WpnAppFixture.php:
--------------------------------------------------------------------------------
1 | 1,
6 | 'name' => 'Valid App',
7 | 'host' => 'localhost',
8 | 'subject' => 'mailto:contact@example.com',
9 | 'public_key' => 'BPNOgWF76BVzSYpRWDWV3SVc9wvZgfvImhhSQNLYGRs_Zfy5rEXb5NITVoUzbEoe9E85oO2830ftuZfwjmHr0FE',
10 | 'private_key' => 'KRbzYHPtD54HPyXP6bkqavNUiTGKls8C0rLyAk7fCoU',
11 | 'enabled' => true,
12 | 'created_at' => date('Y-m-d H:i:s'),
13 | 'updated_at' => date('Y-m-d H:i:s'),
14 | ],
15 | [
16 | 'id' => 2,
17 | 'name' => 'Disabled App',
18 | 'host' => 'localhost2',
19 | 'subject' => 'mailto:contact@example.com',
20 | 'public_key' => 'BPNOgWF76BVzSYpRWDWV3SVc9wvZgfvImhhSQNLYGRs_Zfy5rEXb5NITVoUzbEoe9E85oO2830ftuZfwjmHr0FE',
21 | 'private_key' => 'KRbzYHPtD54HPyXP6bkqavNUiTGKls8C0rLyAk7fCoU',
22 | 'enabled' => false,
23 | 'created_at' => date('Y-m-d H:i:s'),
24 | 'updated_at' => date('Y-m-d H:i:s'),
25 | ],
26 | [
27 | 'id' => 3,
28 | 'name' => 'Valid App with icon',
29 | 'host' => 'localhost3',
30 | 'subject' => 'mailto:contact@example.com',
31 | 'public_key' => 'BPNOgWF76BVzSYpRWDWV3SVc9wvZgfvImhhSQNLYGRs_Zfy5rEXb5NITVoUzbEoe9E85oO2830ftuZfwjmHr0FE',
32 | 'private_key' => 'KRbzYHPtD54HPyXP6bkqavNUiTGKls8C0rLyAk7fCoU',
33 | 'icon' => 'https://www.yiiframework.com/image/design/logo/yii3_sign.png',
34 | 'enabled' => false,
35 | 'created_at' => date('Y-m-d H:i:s'),
36 | 'updated_at' => date('Y-m-d H:i:s'),
37 | ],
38 | ];
--------------------------------------------------------------------------------
/tests/fixtures/data/wpn_campaign.php:
--------------------------------------------------------------------------------
1 | 1,
6 | 'app_id' => 1,
7 | 'title' => 'Push on valid application',
8 | 'tag' => 'push-1',
9 | 'body' => 'My push body',
10 | 'created_at' => date('Y-m-d H:i:s'),
11 | 'scheduled_at' => date('Y-m-d H:i:s'),
12 | 'updated_at' => date('Y-m-d H:i:s'),
13 | ],
14 | [
15 | 'id' => 2,
16 | 'app_id' => 2,
17 | 'title' => 'Push on invalid application',
18 | 'tag' => 'push-2',
19 | 'body' => 'My push body',
20 | 'created_at' => date('Y-m-d H:i:s'),
21 | 'scheduled_at' => date('Y-m-d H:i:s'),
22 | 'updated_at' => date('Y-m-d H:i:s'),
23 | ],
24 | [
25 | 'id' => 3,
26 | 'app_id' => 3,
27 | 'title' => 'Push on application with icon',
28 | 'tag' => 'push-3',
29 | 'body' => 'My push body',
30 | 'created_at' => date('Y-m-d H:i:s'),
31 | 'scheduled_at' => date('Y-m-d H:i:s'),
32 | 'updated_at' => date('Y-m-d H:i:s'),
33 | ],
34 | [
35 | 'id' => 4,
36 | 'app_id' => 1,
37 | 'title' => 'Push for test users only',
38 | 'tag' => 'push-test',
39 | 'body' => 'My test push body',
40 | 'created_at' => date('Y-m-d H:i:s'),
41 | 'scheduled_at' => date('Y-m-d H:i:s'),
42 | 'updated_at' => date('Y-m-d H:i:s'),
43 | ],
44 | ];
--------------------------------------------------------------------------------
/tests/fixtures/data/wpn_subscription.php:
--------------------------------------------------------------------------------
1 | 1,
6 | 'endpoint' => 'endpoint-subscribed-1',
7 | 'auth' => 'auth-1',
8 | 'public_key' => 'public-1',
9 | 'content_encoding' => 'encoding-1',
10 | 'subscribed' => 1,
11 | 'test_user' => 0,
12 | 'app_id' => 1,
13 | 'ip' => '127.0.0.1',
14 | 'created_at' => date('Y-m-d H:i:s'),
15 | 'updated_at' => date('Y-m-d H:i:s'),
16 | ],
17 | [
18 | 'id' => 2,
19 | 'endpoint' => 'endpoint-subscribed-and-test-2',
20 | 'auth' => 'auth-2',
21 | 'public_key' => 'public-2',
22 | 'content_encoding' => 'encoding-2',
23 | 'subscribed' => 1,
24 | 'test_user' => 1,
25 | 'app_id' => 1,
26 | 'ip' => '127.0.0.1',
27 | 'created_at' => date('Y-m-d H:i:s'),
28 | 'updated_at' => date('Y-m-d H:i:s'),
29 | ],
30 | [
31 | 'id' => 3,
32 | 'endpoint' => 'endpoint-not-subscribed-3',
33 | 'auth' => 'auth-3',
34 | 'public_key' => 'public-3',
35 | 'content_encoding' => 'encoding-3',
36 | 'subscribed' => 0,
37 | 'test_user' => 0,
38 | 'app_id' => 1,
39 | 'ip' => '127.0.0.1',
40 | 'created_at' => date('Y-m-d H:i:s'),
41 | 'updated_at' => date('Y-m-d H:i:s'),
42 | ],
43 |
44 | ];
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "lib": [
3 | "dom",
4 | "es5",
5 | "scripthost",
6 | "es2015.promise"
7 | ],
8 | "compilerOptions": {
9 | "target": "es5",
10 | "module": "commonjs",
11 | "esModuleInterop": true,
12 | "experimentalDecorators": true,
13 | "sourceMap": true,
14 | "noEmitOnError": false,
15 | "lib": [
16 | "dom",
17 | "es5",
18 | "scripthost",
19 | "es2015.promise"
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/yii_test:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | run();
21 | exit($exitCode);
22 |
--------------------------------------------------------------------------------