Initial Upload

This commit is contained in:
Matt Batchelder
2025-12-02 10:32:59 -05:00
commit 05ce0da296
2240 changed files with 467811 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboImage;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class AudioWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class AudioWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $audio;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create some media to upload
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/cc0_f1_gp_cars_pass_crash.mp3');
$this->audio = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/cc0_f1_gp_cars_pass_crash.mp3');
// Assign the media we've created to our regions playlist.
$playlist = (new XiboPlaylist($this->getEntityProvider()))->assign([$this->media->mediaId], 10, $layout->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Tidy up the media
$this->media->delete();
$this->audio->delete();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException
*/
public function testEdit()
{
$this->getLogger()->debug('testEdit ' . get_class($this) .' Test');
// Now try to edit our assigned Media Item.
$name = 'Edited Name ' . Random::generateString(5);
$duration = 80;
$useDuration = 1;
$mute = 0;
$loop = 0;
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
'useDuration' => $useDuration,
'mute' => $mute,
'loop' => $loop,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']
);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboImage $widgetOptions */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$widgetOptions = (new XiboImage($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $widgetOptions->name);
$this->assertSame($duration, $widgetOptions->duration);
foreach ($widgetOptions->widgetOptions as $option) {
if ($option['option'] == 'mute') {
$this->assertSame($mute, intval($option['value']));
}
if ($option['option'] == 'loop') {
$this->assertSame($loop, intval($option['value']));
}
if ($option['option'] == 'useDuration') {
$this->assertSame($useDuration, intval($option['value']));
}
}
$this->getLogger()->debug('testEdit finished');
}
/**
* Test to edit and assign an auto item to a widget
*/
public function testAssign()
{
$this->getLogger()->debug('testAssign');
$volume = 80;
$loop = 1;
// Add audio to image assigned to a playlist
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId . '/audio', [
'mediaId' => $this->audio->mediaId,
'volume' => $volume,
'loop' => $loop,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']
);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
/** @var XiboImage $widgetOptions */
$widgetOptions = (new XiboImage($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($this->media->name, $widgetOptions->name);
$this->assertSame($this->media->mediaId, intval($widgetOptions->mediaIds[0]));
$this->assertSame($this->audio->mediaId, intval($widgetOptions->mediaIds[1]));
$this->assertSame($volume, intval($widgetOptions->audio[0]['volume']));
$this->assertSame($loop, intval($widgetOptions->audio[0]['loop']));
$this->getLogger()->debug('testAssign finished');
}
}

View File

@@ -0,0 +1,138 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboClock;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ClockWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class ClockWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/clock/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($name, $duration, $useDuration, $theme, $clockTypeId, $offset, $format, $showSeconds, $clockFace)
* @return array
*/
public function provideSuccessCases()
{
# Sets of data used in testAdd
return [
'Analogue' => ['Api Analogue clock', 20, 1, 1, 1, null, null, 0, 'TwentyFourHourClock'],
'Digital' => ['API digital clock', 20, 1, 0, 2, null, '[HH:mm]', 0, 'TwentyFourHourClock'],
'Flip 24h' => ['API Flip clock 24h', 5, 1, 0, 3, null, null, 1, 'TwentyFourHourClock'],
'Flip counter' => ['API Flip clock Minute counter', 50, 1, 0, 3, null, null, 1, 'MinuteCounter']
];
}
/**
* @param $name
* @param $duration
* @param $useDuration
* @param $theme
* @param $clockTypeId
* @param $offset
* @param $format
* @param $showSeconds
* @param $clockFace
* @dataProvider provideSuccessCases
*/
public function testEdit($name, $duration, $useDuration, $theme, $clockTypeId, $offset, $format, $showSeconds, $clockFace)
{
$response = $this->sendRequest('PUT', '/playlist/widget/' . $this->widgetId, [
'name' => $name,
'useDuration' => $useDuration,
'duration' => $duration,
'themeId' => $theme,
'clockTypeId' => $clockTypeId,
'offset' => $offset,
'format' => $format,
'showSeconds' => $showSeconds,
'clockFace' => $clockFace
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboClock $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboClock($this->getEntityProvider()))->hydrate($response[0]);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'clockTypeId') {
$this->assertSame($clockTypeId, intval($option['value']));
} else {
if ($option['option'] == 'name') {
$this->assertSame($name, $option['value']);
}
}
}
}
}

View File

@@ -0,0 +1,144 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboCurrencies;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class CurrenciesWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class CurrenciesWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/currencies/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($overrideTemplate, $templateId, $name, $duration, $useDuration, $base, $items, $reverseConversion, $effect, $speed, $backgroundColor, $noRecordsMessage, $dateFormat, $updateInterval, $durationIsPerItem, $widgetOriginalWidth, $widgetOriginalHeight, $itemsPerPage, $mainTemplate, $itemTemplate, $styleSheet, $javaScript)
* @return array
*/
public function provideSuccessCases()
{
# Sets of data used in testAdd
return [
'No override template 1' => [false, 'currencies1', 'template 1', 6, 1, 'GBP', 'PLN', 0, NULL, NULL, NULL, 'No messages', NULL, 12, 1, null, null, 5, null, null, null, null],
'No override template 2 reverse' => [false, 'currencies2', 'template 2', 120, 1, 'GBP', 'EUR', 1, NULL, NULL, NULL, 'No messages', NULL, 120, 0, null, null, 2, null, null, null, null],
'Override' => [true, 'currencies1', 'override template', 12, 1, 'GBP', 'EUR', 0, NULL, NULL, NULL, 'No messages', NULL, 60, 1, 1000, 800, 5, '<div class="container-main"><div class="container "><div class="row row-header"><div class="col-2 offset-xs-7 text-center value">BUY</div><div class="col-2 offset-xs-1 value text-center">SELL</div></div><div id="cycle-container">[itemsTemplate]</div></div></div>', '<div class="row row-finance"><div class="col-1 flags"><img class="img-circle center-block " src="[CurrencyFlag]"></div><div class="col-1 value ">[NameShort]</div><div class="col-2 offset-xs-5 text-center value">[Bid]</div><div class="col-2 offset-xs-1 value text-center">[Ask]</div> </div>','body { font-family: "Helvetica", "Arial", sans-serif; line-height: 1; } .container-main {height: 420px !important;width: 820px !important; } .container { height: 420px !important; width: 820px !important; float: left; margin-top: 20px; } .row-finance { height: 60px; background: rgba(0, 0, 0, 0.87); margin-bottom: 20px; } .row {margin-right: 0; margin-left: 0; } .row-header { margin-right: -15px; margin-left: -15px; margin-bottom: 20px; } #cycle-container { margin-left: -15px; margin-right: -15px; } .value { font-size: 20px; padding-top: 20px; font-weight: bold; color: #fff; } .down-arrow { font-size: 20px; color: red; padding-top: 17px; } .up-arrow { font-size: 20px;color: green; padding-top: 17px; } .variant { font-size: 20px; padding-top: 17px; } .flags { padding-top: 4px; } .center-block { width: 50px; height: 50px; }', NULL]
];
}
/**
* This test works correctly, it's marked as broken because we don't have this widget installed by default
* @group broken
* @dataProvider provideSuccessCases
*/
public function testEdit($isOverride, $templateId, $name, $duration, $useDuration, $base, $items, $reverseConversion, $effect, $speed, $backgroundColor, $noRecordsMessage, $dateFormat, $updateInterval, $durationIsPerItem, $widgetOriginalWidth, $widgetOriginalHeight, $itemsPerPage, $mainTemplate, $itemTemplate, $styleSheet, $javaScript)
{
# Edit currency widget and change name, duration, template, reverseConversion and items
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'templateId' => $templateId,
'name' => $name,
'duration' => $duration,
'useDuration' => $useDuration,
'base' => $base,
'items' => $items,
'reverseConversion' => $reverseConversion,
'effect' => $effect,
'speed' => $speed,
'backgroundColor' => $backgroundColor,
'noRecordsMessage' => $noRecordsMessage,
'dateFormat' => $dateFormat,
'updateInterval' => $updateInterval,
'durationIsPerItem' => $durationIsPerItem,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboCurrencies $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboCurrencies($this->getEntityProvider()))->hydrate($response[0]);
# check if changes were correctly saved
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'templateId') {
$this->assertSame($templateId, $option['value']);
}
if ($option['option'] == 'items') {
$this->assertSame($items, $option['value']);
}
if ($option['option'] == 'reverseConversion') {
$this->assertSame($reverseConversion, intval($option['value']));
}
}
}
}

View File

@@ -0,0 +1,168 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDataSet;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DataSetTickerWidgetTest
* @package Xibo\Tests\integration\Widget
*/
class DataSetTickerWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/** @var XiboDataSet */
protected $dataSet;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Add a DataSet
$this->dataSet = (new XiboDataSet($this->getEntityProvider()))->create(Random::generateString(), 'Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/datasetticker/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'step' => 1,
'dataSetId' => $this->dataSet->dataSetId
]);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Delete the DataSet
$this->dataSet->deleteWData();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Edit dataSet ticker
*/
public function testEditDataset()
{
$this->getLogger()->debug('testEdit ' . get_class($this) .' Test');
// Edit ticker
$noDataMessage = 'no records found';
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => 'Edited widget',
'duration' => 90,
'useDuration' => 1,
'updateInterval' => 100,
'effect' => 'fadeout',
'speed' => 500,
'template' => '[Col1]',
'durationIsPerItem' => 1,
'itemsSideBySide' => 1,
'upperLimit' => 0,
'lowerLimit' => 0,
'itemsPerPage' => 5,
'noDataMessage' => $noDataMessage
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Incorrect status: ' . $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->getLogger()->debug('Request successful, double check contents.');
/** @var XiboTicker $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboTicker($this->getEntityProvider()))->hydrate($response[0]);
// check if changes were correctly saved
$this->assertSame('Edited widget', $checkWidget->name);
$this->assertSame(90, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'updateInterval') {
$this->assertSame(100, intval($option['value']));
}
if ($option['option'] == 'effect') {
$this->assertSame('fadeout', $option['value']);
}
if ($option['option'] == 'speed') {
$this->assertSame(500, intval($option['value']));
}
if ($option['option'] == 'template') {
$this->assertSame('[Col1]', $option['value']);
}
if ($option['option'] == 'durationIsPerItem') {
$this->assertSame(1, intval($option['value']));
}
if ($option['option'] == 'itemsSideBySide') {
$this->assertSame(1, intval($option['value']));
}
if ($option['option'] == 'upperLimit') {
$this->assertSame(0, intval($option['value']));
}
if ($option['option'] == 'lowerLimit') {
$this->assertSame(0, intval($option['value']));
}
if ($option['option'] == 'itemsPerPage') {
$this->assertSame(5, intval($option['value']));
}
if ($option['option'] == 'noDataMessage') {
$this->assertSame($noDataMessage, $option['value']);
}
}
$this->getLogger()->debug('testEdit finished');
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDataSet;
use Xibo\OAuth2\Client\Entity\XiboDataSetColumn;
use Xibo\OAuth2\Client\Entity\XiboDataSetView;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
class DataSetViewWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/** @var XiboDataSet */
protected $dataSet;
/** @var XiboDataSetColumn */
protected $dataSetColumn;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Add a DataSet
$this->dataSet = (new XiboDataSet($this->getEntityProvider()))->create(Random::generateString(), 'Test');
// Create a Column for our DataSet
$this->dataSetColumn = (new XiboDataSetColumn($this->getEntityProvider()))->create($this->dataSet->dataSetId, Random::generateString(8, 'phpunit'),'', 2, 1, 1, '');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/datasetview/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'step' => 1,
'dataSetId' => $this->dataSet->dataSetId
]);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Delete the DataSet
$this->dataSet->deleteWData();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
public function testEdit()
{
$nameNew = 'Edited Name';
$durationNew = 80;
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'dataSetColumnId' => [$this->dataSetColumn->dataSetColumnId],
'name' => $nameNew,
'duration' => $durationNew,
'updateInterval' => 100,
'rowsPerPage' => 2,
'showHeadings' => 0,
'upperLimit' => 0,
'lowerLimit' => 0,
'filter' => null,
'ordering' => null,
'templateId' => 'light-green',
'overrideTemplate' => 0,
'useOrderingClause' => 0,
'useFilteringClause' => 0,
'noDataMessage' => 'No Data returned',
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboDataSetView $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboDataSetView($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($nameNew, $checkWidget->name);
$this->assertSame($durationNew, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'templateId') {
$this->assertSame('light-green', $option['value']);
} else if ($option['option'] == 'updateInterval') {
$this->assertSame(100, intval($option['value']));
} else if ($option['option'] == 'name') {
$this->assertSame($nameNew, $option['value']);
}
}
}
}

View File

@@ -0,0 +1,108 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboEmbedded;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class EmbeddedWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class EmbeddedWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/embedded/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException
*/
public function testEdit()
{
$name = Random::generateString();
$durationNew = 80;
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $durationNew,
'transparency' => 1,
'scaleContent' => 1,
'embedHtml' => null,
'embedScript' => null,
'embedStyle' => '<style type="text/css"> </style>'
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboEmbedded $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboEmbedded($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($durationNew, $checkWidget->duration);
}
}

View File

@@ -0,0 +1,149 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboGoogleTraffic;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class GoogleTrafficWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class GoogleTrafficWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
parent::installModuleIfNecessary('googletraffic', '\Xibo\Widget\GoogleTraffic');
}
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/googletraffic/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($name, $duration, $useDisplayLocation, $longitude, $latitude, $zoom)
* @return array
*/
public function provideEditCases()
{
# Sets of data used in testAdd
return [
'Use Display location' => [200, 'Traffic with display location', 2000, 1, null, null, 100],
'Custom location 1' => [200, 'Traffic with custom location - Italy', 4500, 0, 7.640974, 45.109612, 80],
'Custom location 2' => [200, 'Traffic with custom location - Japan', 4500, 0, 35.7105, 139.7336, 50],
'No zoom provided' => [422, 'no zoom', 2000, 1, null, null, null],
'no lat/long' => [422, 'no lat/long provided with useDisplayLocation 0', 3000, 0, null, null, 20],
'low min duration' => [422, 'Traffic with display location', 20, 1, null, null, 100],
];
}
/**
* Edit
* @dataProvider provideEditCases
* This test works correctly, it's marked as broken because we don't have this widget installed by default
* @group broken
*/
public function testEdit($statusCode, $name, $duration, $useDisplayLocation, $lat, $long, $zoom)
{
$this->getLogger()->debug('testEdit ' . get_class($this) .' Test');
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
'useDuration' => 1,
'useDisplayLocation' => $useDisplayLocation,
'longitude' => $long,
'latitude' => $lat,
'zoom' => $zoom,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame($statusCode, $response->getStatusCode(), 'Incorrect status code.', var_export($response, true));
if ($statusCode == 422)
return;
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboGoogleTraffic $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboGoogleTraffic($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'longitude' && $long !== null) {
$this->assertSame($long, floatval($option['value']));
}
if ($option['option'] == 'latitude' && $lat !== null) {
$this->assertSame($lat, floatval($option['value']));
}
if ($option['option'] == 'zoom') {
$this->assertSame($zoom, intval($option['value']));
}
}
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboHls;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class HlsWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class HlsWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
parent::installModuleIfNecessary('hls', '\Xibo\Widget\Hls');
}
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/hls/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
$this->getLogger()->debug('Setup Finished');
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($name, $useDuration, $duration, $uri, $mute, $transparency)
* @return array
*/
public function provideEditCases()
{
# Sets of data used in testAdd
return [
'HLS stream' => [200, 'HLS stream', 1, 20, 'http://ceu.xibo.co.uk/hls/big_buck_bunny_adaptive_master.m3u8', 0, 0],
'HLS stream 512' => [200, 'HLS stream with transparency', 1, 20, 'http://ceu.xibo.co.uk/hls/big_buck_bunny_adaptive_512.m3u8', 0, 1],
'No url provided' => [422, 'no uri', 1, 10, '', 0, 0],
'No duration provided' => [422, 'no duration with useDuration 1', 1, 0, 'http://ceu.xibo.co.uk/hls/big_buck_bunny_adaptive_512.m3u8', 0, 0],
];
}
/**
* Edit
* @dataProvider provideEditCases
*/
public function testEdit($statusCode, $name, $useDuration, $duration, $uri, $mute, $transparency)
{
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'useDuration' => $useDuration,
'duration' => $duration,
'uri' => $uri,
'mute' => $mute,
'transparency' => $transparency,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame($statusCode, $response->getStatusCode());
if ($statusCode == 422)
return;
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboHls $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboHls($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'uri') {
$this->assertSame($uri, urldecode(($option['value'])));
}
}
}
}

View File

@@ -0,0 +1,135 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboImage;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ImageWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class ImageWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var XiboLibrary */
protected $media;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create some media to upload
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/xts-night-001.jpg');
// Assign the media we've created to our regions playlist.
$playlist = (new XiboPlaylist($this->getEntityProvider()))->assign([$this->media->mediaId], 10, $layout->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Tidy up the media
$this->media->delete();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
public function testEdit()
{
$this->getLogger()->debug('testEdit ' . get_class($this) .' Test');
// Edit properties
$nameNew = 'Edited Name: ' . Random::generateString(5);
$durationNew = 80;
$scaleTypeIdNew = 'stretch';
$alignIdNew = 'center';
$valignIdNew = 'top';
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $nameNew,
'duration' => $durationNew,
'useDuration' => 1,
'scaleTypeId' => $scaleTypeIdNew,
'alignId' => $alignIdNew,
'valignId' => $valignIdNew,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboImage $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboImage($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($nameNew, $checkWidget->name);
$this->assertSame($durationNew, $checkWidget->duration);
$this->assertSame($this->media->mediaId, intval($checkWidget->mediaIds[0]));
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'scaleTypeId') {
$this->assertSame($scaleTypeIdNew, $option['value']);
}
if ($option['option'] == 'alignId') {
$this->assertSame($alignIdNew, $option['value']);
}
if ($option['option'] == 'valignId') {
$this->assertSame($valignIdNew, $option['value']);
}
}
}
}

View File

@@ -0,0 +1,128 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboLocalVideo;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LocalVideoWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class LocalVideoWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/localvideo/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($name, $duration, $theme, $clockTypeId, $offset, $format, $showSeconds, $clockFace)
* @return array
*/
public function provideEditCases()
{
# Sets of data used in testAdd
return [
'Aspect' => ['rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov', 30, 1, 'aspect', 0],
'Stretch muted' => ['rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov', 100, 1, ' stretch', 1],
];
}
/**
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException
* @dataProvider provideEditCases
*/
public function testEdit($uri, $duration, $useDuration, $scaleTypeId, $mute)
{
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'uri' => $uri,
'duration' => $duration,
'useDuration' => $useDuration,
'scaleTypeId' => $scaleTypeId,
'mute' => $mute,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboLocalVideo $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboLocalVideo($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'uri') {
$this->assertSame($uri, urldecode($option['value']));
}
if ($option['option'] == 'scaleTypeId') {
$this->assertSame($scaleTypeId, $option['value']);
}
if ($option['option'] == 'mute') {
$this->assertSame($mute, intval($option['value']));
}
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LocalVideoWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class MenuBoardWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
private $menuBoard;
private $menuBoardCategory;
private $menuBoardProduct;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/menuboard/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->menuBoard = $this->getEntityProvider()->post('/menuboard', [
'name' => 'phpunit Menu board',
'description' => 'Description for test Menu Board'
]);
$this->menuBoardCategory = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoard['menuId'] . '/category', [
'name' => 'phpunit Menu Board Category'
]);
$this->menuBoardProduct = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoardCategory['menuCategoryId'] . '/product', [
'name' => 'phpunit Menu Board Product',
'price' => '$11.11'
]);
$this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'step' => 1,
'menuId' => $this->menuBoard['menuId'],
'templateId' => 'menuboard1'
]);
$this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'step' => 2,
'menuBoardCategories_1' => [$this->menuBoardCategory['menuCategoryId']]
]);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
if ($this->menuBoard['menuId'] !== null) {
$this->getEntityProvider()->delete('/menuboard/' . $this->menuBoard['menuId']);
}
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
public function testEdit()
{
$response = $this->sendRequest('PUT', '/playlist/widget/' . $this->widgetId, [
'name' => 'Test Menu Board Widget',
'duration' => 60,
'useDuration' => 1,
'showUnavailable' => 0,
'productsHighlight' => [$this->menuBoardProduct['menuProductId']]
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$widget = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId])[0];
$this->assertSame(60, $widget['duration']);
foreach ($widget['widgetOptions'] as $option) {
if ($option['option'] == 'showUnavailable') {
$this->assertSame(0, intval($option['value']));
} elseif ($option['option'] == 'name') {
$this->assertSame('Test Menu Board Widget', $option['value']);
} elseif ($option['option'] == 'productsHighlight') {
$this->assertSame([$this->menuBoardProduct['menuBoardProductId']], $option['value']);
}
}
}
}

View File

@@ -0,0 +1,122 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPdf;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class PdfWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class PdfWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var XiboLibrary */
protected $media;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create some media to upload
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/sampleDocument.pdf');
// Assign the media we've created to our regions playlist.
$playlist = (new XiboPlaylist($this->getEntityProvider()))->assign([$this->media->mediaId], 10, $layout->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Tidy up the media
$this->media->delete();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Test Edit
*/
public function testEdit()
{
$this->getLogger()->debug('testEdit ' . get_class($this) .' Test');
$name = 'Edited Name: ' . Random::generateString(5);
$duration = 80;
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->getLogger()->debug('testEdit Finished');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboPdf $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboPdf($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
$this->assertSame($this->media->mediaId, intval($checkWidget->mediaIds[0]));
$this->getLogger()->debug('testEdit Finished');
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCommand;
use Xibo\OAuth2\Client\Entity\XiboShellCommand;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ShellCommandWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class ShellCommandWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var XiboCommand */
protected $command;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/shellcommand/' . $layout->regions[0]->regionPlaylist->playlistId);
// Create a command
$this->command = (new XiboCommand($this->getEntityProvider()))->create(Random::generateString(), 'phpunit description', 'phpunitcode');
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Delete the commant
$this->command->delete();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($name, $duration, $windowsCommand, $linuxCommand, $launchThroughCmd, $terminateCommand, $useTaskkill, $commandCode)
* @return array
*/
public function provideSuccessCases()
{
# Sets of data used in testAdd
return [
'Windows new command' => ['Api Windows command', 20, 1,'reboot', NULL, 1, null, 1, null],
'Android new command' => ['Api Android command', 30, 1, null, 'reboot', null, 1, null, null],
'Previously created command' => ['Api shell command', 50, 1, null, null, 1, 1, 1, 'phpunit code']
];
}
/**
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException
* @dataProvider provideSuccessCases
*/
public function testEdit($name, $duration, $useDuration, $windowsCommand, $linuxCommand, $launchThroughCmd, $terminateCommand, $useTaskkill, $commandCode)
{
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
'useDuration' => $useDuration,
'windowsCommand' => $windowsCommand,
'linuxCommand' => $linuxCommand,
'launchThroughCmd' => $launchThroughCmd,
'terminateCommand' => $terminateCommand,
'useTaskkill' => $useTaskkill,
'commandCode' => $commandCode,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboShellCommand $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboShellCommand($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'commandCode') {
$this->assertSame($commandCode, $option['value']);
}
}
}
}

View File

@@ -0,0 +1,128 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class TextWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class TextWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format ($name, $duration, $useDuration, $effect, $speed, $backgroundColor, $marqueeInlineSelector, $text, $javaScript)
* @return array
*/
public function provideSuccessCases()
{
// Sets of data used in testAdd
return [
'text 1' => ['Text item', 10, 1, 'marqueeRight', 5, null, null, 'TEST API TEXT', null],
'text with formatting' => ['Text item 2', 20, 1, 'marqueeLeft', 3, null, null, '<p><span style=color:#FFFFFF;><span style=font-size:48px;>TEST</span></span></p>', null],
'text with background Colour' => ['text item 3', 5, 1, null, 0, '#d900000', null, 'red background', null]
];
}
/**
* Test Edit
* @dataProvider provideSuccessCases
*/
public function testEdit($name, $duration, $useDuration, $effect, $speed, $backgroundColor, $marqueeInlineSelector, $text, $javaScript)
{
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
'useDuration' => $useDuration,
'effect' => $effect,
'speed' => $speed,
'backgroundColor' => $backgroundColor,
'marqueeInlineSelector' => $marqueeInlineSelector,
'text' => $text,
'javaScript' => $javaScript
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboText $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboText($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'text') {
$this->assertSame($text, $option['value']);
}
}
}
}

View File

@@ -0,0 +1,359 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\OAuth2\Client\Exception\XiboApiException;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class TickerWidgetTest
* tests the Ticker Widget Module
* @package Xibo\Tests\Integration\Widget
*/
class TickerWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
// Copy the rss resources folder into web
shell_exec('cp -r ' . PROJECT_ROOT . '/tests/resources/rss ' . PROJECT_ROOT . '/web');
}
public static function tearDownAfterClass()
{
shell_exec('rm -r ' . PROJECT_ROOT . '/web/rss');
parent::tearDownAfterClass();
}
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/ticker/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetLicensed($this->display);
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->publishedLayout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
//</editor-fold>
/**
* @return array
*/
public function providerEditTest()
{
return [
'Duration is per item with num items specified (higher than actual) with copyright' => [
[
'uri' => 'http://localhost/rss/feed.xml',
'name' => 'Edited widget',
'duration' => 90,
'useDuration' => 1,
'numItems' => 10,
'durationIsPerItem' => 1,
'updateInterval' => 100,
'effect' => 'fade',
'speed' => 5,
'copyright' => 'Copyrights ©Xibo Signage',
'itemsSideBySide' => 0,
'upperLimit' => 0,
'lowerLimit' => 0,
'itemsPerPage' => 1,
'stripTags' => 'p',
'textDirection' => 'ltr',
'overrideTemplate' => 0,
'templateId' => 'media-rss-with-title',
'noDataMessage' => 'no records found'
],
90*5
],
'Duration is per item with num items specified (higher than actual) without copyright' => [
[
'uri' => 'http://localhost/rss/feed.xml',
'name' => 'Edited widget',
'duration' => 90,
'useDuration' => 1,
'numItems' => 10,
'durationIsPerItem' => 1,
'updateInterval' => 100,
'effect' => 'fade',
'speed' => 5,
'copyright' => null,
'itemsSideBySide' => 0,
'upperLimit' => 0,
'lowerLimit' => 0,
'itemsPerPage' => 1,
'stripTags' => 'p',
'textDirection' => 'ltr',
'overrideTemplate' => 0,
'templateId' => 'media-rss-with-title',
'noDataMessage' => 'no records found'
],
90*4
],
'Duration is per item with num items specified (lower than actual)' => [
[
'uri' => 'http://localhost/rss/feed.xml',
'name' => 'Edited widget',
'duration' => 90,
'useDuration' => 1,
'numItems' => 2,
'durationIsPerItem' => 1,
'updateInterval' => 100,
'effect' => 'fade',
'speed' => 5,
'copyright' => null,
'itemsSideBySide' => 0,
'upperLimit' => 0,
'lowerLimit' => 0,
'itemsPerPage' => 1,
'stripTags' => 'p',
'textDirection' => 'ltr',
'overrideTemplate' => 0,
'templateId' => 'media-rss-with-title',
'noDataMessage' => 'no records found'
],
90*2
],
'Duration not per item with num items specified' => [
[
'uri' => 'http://localhost/rss/feed.xml',
'name' => 'Edited widget',
'duration' => 90,
'useDuration' => 1,
'numItems' => 2,
'durationIsPerItem' => 0,
'updateInterval' => 100,
'effect' => 'fade',
'speed' => 5,
'copyright' => null,
'itemsSideBySide' => 0,
'upperLimit' => 0,
'lowerLimit' => 0,
'itemsPerPage' => 1,
'stripTags' => 'p',
'textDirection' => 'ltr',
'overrideTemplate' => 0,
'templateId' => 'media-rss-with-title',
'noDataMessage' => 'no records found'
],
90
],
'Default Duration' => [
[
'uri' => 'http://localhost/rss/feed.xml',
'name' => 'Edited widget',
'duration' => 90,
'useDuration' => 0,
'numItems' => 0,
'durationIsPerItem' => 0,
'updateInterval' => 100,
'effect' => 'fade',
'speed' => 5,
'copyright' => 'Copyrights ©Xibo Signage',
'itemsSideBySide' => 0,
'upperLimit' => 0,
'lowerLimit' => 0,
'itemsPerPage' => 1,
'stripTags' => 'p',
'textDirection' => 'ltr',
'overrideTemplate' => 0,
'templateId' => 'media-rss-with-title',
'noDataMessage' => 'no records found'
],
5
]
];
}
/**
* Edit rss feed ticker
* @dataProvider providerEditTest
* @param array $newWidgetOptions
* @param int $expectedDuration What is the expected duration in rendered output?
* @throws XiboApiException
*/
public function testEditFeed($newWidgetOptions, $expectedDuration)
{
$this->getLogger()->debug('testEditFeed - IN');
// Edit ticker widget
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, $newWidgetOptions, ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->getLogger()->debug('Check Response');
// Check response
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
// Get the edited ticker back out again.
/** @var XiboTicker $editedTicker */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$editedTicker = (new XiboTicker($this->getEntityProvider()))->hydrate($response[0]);
// check if changes were correctly saved
$this->assertSame('Edited widget', $editedTicker->name);
$this->assertSame(90, $editedTicker->duration);
foreach ($editedTicker->widgetOptions as $option) {
if ($option['option'] == 'uri') {
$this->assertSame($newWidgetOptions['uri'], urldecode($option['value']));
} else if ($option['option'] == 'updateInterval') {
$this->assertSame($newWidgetOptions['updateInterval'], intval($option['value']));
} else if ($option['option'] == 'effect') {
$this->assertSame($newWidgetOptions['effect'], $option['value']);
} else if ($option['option'] == 'speed') {
$this->assertSame($newWidgetOptions['speed'], intval($option['value']));
} else if ($option['option'] == 'copyright') {
$this->assertSame($newWidgetOptions['copyright'], $option['value']);
} else if ($option['option'] == 'numItems') {
$this->assertSame($newWidgetOptions['numItems'], intval($option['value']));
} else if ($option['option'] == 'durationIsPerItem') {
$this->assertSame($newWidgetOptions['durationIsPerItem'], intval($option['value']));
} else if ($option['option'] == 'itemsSideBySide') {
$this->assertSame($newWidgetOptions['itemsSideBySide'], intval($option['value']));
} else if ($option['option'] == 'stripTags') {
$this->assertSame($newWidgetOptions['stripTags'], $option['value']);
} else if ($option['option'] == 'textDirection') {
$this->assertSame($newWidgetOptions['textDirection'], $option['value']);
} else if ($option['option'] == 'templateId') {
$this->assertSame($newWidgetOptions['templateId'], $option['value']);
} else if ($option['option'] == 'noDataMessage') {
$this->assertSame($newWidgetOptions['noDataMessage'], $option['value']);
}
}
$this->getLogger()->debug('Test Complete - publish and validate');
// Publish
$this->publishedLayout = $this->publish($this->publishedLayout);
$layout = $this->publishedLayout;
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $layout->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
$this->assertContains('layoutid="' . $layout->layoutId . '"', $rf, 'Layout not in Required Files');
// Call getResource
// Parse the output, check the "duration" field.
$this->getLogger()->debug('Calling GetResource - for ' . $layout->layoutId . ' - ' . $layout->regions[0]->regionId . ' - ' . $editedTicker->widgetId);
$html = null;
try {
$html = $this->getXmdsWrapper()->GetResource($this->display->license, $layout->layoutId, $layout->regions[0]->regionId, $editedTicker->widgetId);
$this->getLogger()->debug('Get Resource Complete');
} catch (\Exception $exception) {
$this->getLogger()->error($html);
$this->fail($exception->getMessage());
}
// Parse the HTML for the expected duration is per item
$matches = [];
preg_match('<!-- DURATION=(.*?) -->', $html, $matches);
$this->getLogger()->debug('Matches: ' . var_export($matches, true));
$this->assertEquals(2, count($matches), 'More than 1 duration tag in HTML');
$this->assertEquals($expectedDuration, intval($matches[1]), 'Duration doesn\'t match expected duration');
$this->getLogger()->debug('Test Complete');
}
}

View File

@@ -0,0 +1,255 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboVideo;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class VideoWidgetTest
* @package Xibo\Tests\Integration\Widget
*/
class VideoWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $draftLayout;
/** @var XiboLibrary */
protected $media;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$this->draftLayout = $this->getDraft($this->publishedLayout);
// Create some media to upload
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/HLH264.mp4');
// Assign the media we've created to our regions playlist.
$playlist = (new XiboPlaylist($this->getEntityProvider()))->assign([$this->media->mediaId], 10, $this->draftLayout->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
// Tidy up the media
$this->media->delete();
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
public function testEdit()
{
$name = 'Edited Name: ' . Random::generateString(5);
$useDuration = 1;
$duration = 80;
$scaleTypeId = 'stretch';
$mute = 1;
$loop = 0;
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
'useDuration' => $useDuration,
'scaleTypeId' => $scaleTypeId,
'mute' => $mute,
'loop' => $loop,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboVideo $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboVideo($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
$this->assertSame($this->media->mediaId, intval($checkWidget->mediaIds[0]));
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'scaleTypeId') {
$this->assertSame($scaleTypeId, $option['value']);
}
if ($option['option'] == 'mute') {
$this->assertSame($mute, intval($option['value']));
}
if ($option['option'] == 'loop') {
$this->assertSame($loop, intval($option['value']));
}
if ($option['option'] == 'useDuration') {
$this->assertSame($useDuration, $option['value']);
}
}
}
public function testEndDetect()
{
$response = $this->sendRequest('PUT', '/playlist/widget/' . $this->widgetId, [
'name' => 'End Detect',
'duration' => 0,
'useDuration' => 0,
'scaleTypeId' => 'aspect',
'mute' => 1,
'loop' => 0,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
// Publish
$this->publishedLayout = $this->publish($this->publishedLayout);
// Build the Layout and get the XLF
$this->buildLayout($this->publishedLayout);
// Create a Display
$display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
date('Y-m-d H:i:s', time()),
date('Y-m-d H:i:s', time()+7200),
$this->publishedLayout->campaignId,
[$display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetLicensed($display);
$this->getXmdsWrapper()->RequiredFiles($display->license);
// Get the file XLF
$file = $this->getXmdsWrapper()->GetFile($display->license,
$this->publishedLayout->layoutId,
'layout',
0,
1024
);
$this->getLogger()->debug($file);
$this->assertTrue(stripos($file, 'duration="0"') !== false, 'Duration is not 0 as expected for End Detection');
$this->assertTrue(stripos($file, 'useDuration="0"') !== false, 'useDuration is incorrectly set');
}
public function testNotEndDetect()
{
$response = $this->sendRequest('PUT', '/playlist/widget/' . $this->widgetId, [
'name' => 'End Detect',
'duration' => 35,
'useDuration' => 1,
'scaleTypeId' => 'aspect',
'mute' => 1,
'loop' => 0,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
// Publish
$this->publishedLayout = $this->publish($this->publishedLayout);
// Build the Layout and get the XLF
$this->buildLayout($this->publishedLayout);
// Create a Display
$display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
date('Y-m-d H:i:s', time()),
date('Y-m-d H:i:s', time()+7200),
$this->publishedLayout->campaignId,
[$display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetLicensed($display);
$this->getXmdsWrapper()->RequiredFiles($display->license);
// Get the file XLF
$file = $this->getXmdsWrapper()->GetFile($display->license,
$this->publishedLayout->layoutId,
'layout',
0,
1024
);
$this->getLogger()->debug($file);
$this->assertTrue(stripos($file, 'duration="35"') !== false, 'Duration is not > 0 as expected for non End Detection');
$this->assertTrue(stripos($file, 'useDuration="1"') !== false, 'useDuration is incorrectly set');
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboWebpage;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
class WebpageWidgetTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a Widget for us to edit.
$response = $this->getEntityProvider()->post('/playlist/widget/webpage/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->widgetId = $response['widgetId'];
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
parent::tearDown();
$this->getLogger()->debug('Tear down for ' . get_class($this) .' Test');
}
/**
* Each array is a test run
* Format (($name, $duration, $useDuration, $transparency, $uri, $scaling, $offsetLeft, $offsetTop, $pageWidth, $pageHeight, $modeId)
* @return array
*/
public function provideSuccessCases()
{
# Sets of data used in testAdd
return [
'Open natively' => ['Open natively webpage widget', 60, 1, NULL, 'http://xibo.org.uk/', NULL, NULL, NULL, NULL, NULL, 1],
'Manual Position default' => ['Manual Position default values', 10, 1, NULL, 'http://xibo.org.uk/', NULL, NULL, NULL, NULL, NULL, 2],
'Manual Position custom' => ['Manual Position custom values', 100, 1, 1, 'http://xibo.org.uk/', 100, 200, 100, 1600, 900, 2],
'Best Fit default' => ['Best Fit webpage widget', 10, 1, NULL, 'http://xibo.org.uk/', NULL, NULL, NULL, NULL, NULL, 3],
'Best Fit custom' => ['Best Fit webpage widget', 150, 1, NULL, 'http://xibo.org.uk/', NULL, NULL, 1920, 1080, NULL, 3],
];
}
/**
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException
* @dataProvider provideSuccessCases
*/
public function testEdit($name, $duration, $useDuration, $transparency, $uri, $scaling, $offsetLeft, $offsetTop, $pageWidth, $pageHeight, $modeId)
{
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'name' => $name,
'duration' => $duration,
'useDuration' => $useDuration,
'transparency' => $transparency,
'uri' => $uri,
'scaling' => $scaling,
'offsetLeft' => $offsetLeft,
'offsetTop' => $offsetTop,
'pageWidth' => $pageWidth,
'pageHeight' => $pageHeight,
'modeId' => $modeId
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboWebpage $checkWidget */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$checkWidget = (new XiboWebpage($this->getEntityProvider()))->hydrate($response[0]);
$this->assertSame($name, $checkWidget->name);
$this->assertSame($duration, $checkWidget->duration);
foreach ($checkWidget->widgetOptions as $option) {
if ($option['option'] == 'uri') {
$this->assertSame($uri, urldecode($option['value']));
}
if ($option['option'] == 'modeId') {
$this->assertSame($modeId, intval($option['value']));
}
}
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Widget;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class WidgetAddTest
* @package Xibo\Tests\integration\Widget
*/
class WidgetAddTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $layout;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$this->layout = $this->getDraft($this->publishedLayout);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
}
// </editor-fold>
/**
* Test add a widget
*/
public function testAdd()
{
$playlistId = $this->layout->regions[0]->regionPlaylist->playlistId;
$this->getLogger()->debug('testAdd - ' . $playlistId);
$response = $this->sendRequest('POST','/playlist/widget/text/' . $playlistId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->getLogger()->debug('testAdd - finished.');
}
/**
* Test adding a non-region specific widget using the region specific widget add call
*/
public function testAddNonRegionSpecific()
{
$playlistId = $this->layout->regions[0]->regionPlaylist->playlistId;
$this->getLogger()->debug('testAddNonRegionSpecific - ' . $playlistId);
$response = $this->sendRequest('POST','/playlist/widget/audio/' . $playlistId);
$this->assertSame(422, $response->getStatusCode(), 'Status Code isnt correct: ' . $response->getStatusCode());
$this->getLogger()->debug('testAddNonRegionSpecific - finished.');
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Widget;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class WidgetDeleteTest
* @package Xibo\Tests\integration\Widget
*/
class WidgetDeleteTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $publishedLayout;
/** @var int */
protected $widgetId;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup for ' . get_class($this) .' Test');
// Create a Layout
$this->publishedLayout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->publishedLayout);
// Create a widget
$response = $this->getEntityProvider()->post('/playlist/widget/datasetview/' . $layout->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $response['widgetId'];
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->publishedLayout);
}
// </editor-fold>
/**
* Test edit a widget
*/
public function testDelete()
{
// Delete the widget we've added
$response = $this->sendRequest('DELETE','/playlist/widget/' . $this->widgetId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Widget;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class WidgetOnDraftsTest
* @package Xibo\Tests\integration\Widget
*/
class WidgetOnDraftsTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout */
private $layout;
public function setup()
{
parent::setup();
$this->layout = $this->createLayout();
}
public function tearDown()
{
// This should always be the original, regardless of whether we checkout/discard/etc
$this->layout->delete();
parent::tearDown();
}
/**
* Test to try and add a widget to a Published Layout
*/
public function testEditPublished()
{
// Get my Playlist
$playlistId = $this->layout->regions[0]->regionPlaylist->playlistId;
// Add a widget (any widget will do, it doesn't matter)
$response = $this->sendRequest('POST','/playlist/widget/localVideo/' . $playlistId);
$this->getLogger()->debug('Response from Widget Add is ' . $response->getBody()->getContents());
$this->assertSame(422, $response->getStatusCode(), $response->getBody());
}
/**
* Test to try and add a widget to a Published Layout
*/
public function testEditDraft()
{
// Checkout the Layout
$layout = $this->getDraft($this->layout);
// Get my Playlist
$playlistId = $layout->regions[0]->regionPlaylist->playlistId;
// Add a widget (any widget will do, it doesn't matter)
$response = $this->sendRequest('POST','/playlist/widget/localVideo/' . $playlistId);
$this->getLogger()->debug('Response from Widget Add is ' . $response->getBody()->getContents());
$this->assertSame(200, $response->getStatusCode(), $response);
}
}