Skip to content

Commit

Permalink
MDL-63399 behat: implement a new download step
Browse files Browse the repository at this point in the history
  • Loading branch information
lameze committed May 29, 2024
1 parent d32844c commit fe67ce3
Show file tree
Hide file tree
Showing 24 changed files with 263 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class behat_admin_presets extends behat_base {
*/
final public function following_in_the_should_download_between_and_bytes(string $link, string $selectortype,
string $nodeelement, string $nodeselectortype, int $minexpectedsize, int $maxexpectedsize): void {
// TO BE DEPRECATED.
// If the minimum is greater than the maximum then swap the values.
if ((int) $minexpectedsize > (int) $maxexpectedsize) {
list($minexpectedsize, $maxexpectedsize) = [$maxexpectedsize, $minexpectedsize];
Expand Down
10 changes: 7 additions & 3 deletions admin/tool/admin_presets/tests/behat/download.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ Feature: I can download a preset
| name |
| Custom preset |

@javascript
@javascript
Scenario: Custom preset settings can be downloaded
Given I log in as "admin"
And I navigate to "Site admin presets" in site administration
When I open the action menu in "Custom preset" "table_row"
Then following "Download" "link" in the "Custom preset" "table_row" should download between "0" and "5000" bytes
# Failing.
And I pause
Then following "Download" should download a "xml" file that:
| Contains | Custom preset |

@javascript
Scenario: Core preset settings can be downloaded
Given I log in as "admin"
And I navigate to "Site admin presets" in site administration
When I open the action menu in "Starter" "table_row"
Then following "Download" "link" in the "Starter" "table_row" should download between "0" and "5000" bytes
Then following "Download" should download a "xml" file that:
| Contains | Starter |
18 changes: 12 additions & 6 deletions admin/tool/dataprivacy/tests/behat/dataexport.feature
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ Feature: Data export from the privacy API
And I reload the page
And I should see "Download ready" in the "Victim User 1" "table_row"
And I open the action menu in "Victim User 1" "table_row"
And following "Download" should download between "1" and "200000" bytes
And following "Download" should download a "zip" file that:
| Contains | index.html |
And the following config values are set as admin:
| privacyrequestexpiry | 1 | tool_dataprivacy |
And I wait "1" seconds
Expand Down Expand Up @@ -89,7 +90,8 @@ Feature: Data export from the privacy API
And I reload the page
And I should see "Download ready" in the "Export all of my personal data" "table_row"
And I open the action menu in "Victim User 1" "table_row"
And following "Download" should download between "1" and "200000" bytes
And following "Download" should download a "zip" file that:
| Contains | index.html |

And the following config values are set as admin:
| privacyrequestexpiry | 1 | tool_dataprivacy |
Expand Down Expand Up @@ -126,7 +128,8 @@ Feature: Data export from the privacy API
And I reload the page
And I should see "Download ready" in the "Victim User 1" "table_row"
And I open the action menu in "Victim User 1" "table_row"
And following "Download" should download between "1" and "200000" bytes
And following "Download" should download a "zip" file that:
| Contains | index.html |

And the following config values are set as admin:
| privacyrequestexpiry | 1 | tool_dataprivacy |
Expand Down Expand Up @@ -185,7 +188,8 @@ Feature: Data export from the privacy API
And I reload the page
And I should see "Download ready" in the "Victim User 1" "table_row"
And I open the action menu in "Victim User 1" "table_row"
And following "Download" should download between "1" and "170000" bytes
Then following "Download" should download a "zip" file that:
| Contains | index.html |
And the following config values are set as admin:
| privacyrequestexpiry | 1 | tool_dataprivacy |
And I wait "1" seconds
Expand Down Expand Up @@ -229,7 +233,8 @@ Feature: Data export from the privacy API
And I reload the page
And I should see "Download ready" in the "Victim User 1" "table_row"
And I open the action menu in "Victim User 1" "table_row"
And following "Download" should download between "1" and "180000" bytes
And following "Download" should download a "zip" file that:
| Contains | index.html |

@javascript
Scenario: Filter before export data for a user and download it in the view request action
Expand Down Expand Up @@ -262,4 +267,5 @@ Feature: Data export from the privacy API
And I reload the page
And I should see "Download ready" in the "Victim User 1" "table_row"
And I open the action menu in "Victim User 1" "table_row"
And following "Download" should download between "1" and "180000" bytes
And following "Download" should download a "zip" file that:
| Contains | index.html |
3 changes: 2 additions & 1 deletion analytics/tests/behat/manage_models.feature
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ Feature: Manage analytics models
When I open the action menu in "Students at risk of not meeting the course completion conditions" "table_row"
And I choose "Export" in the open action menu
And I click on "Actions" "link" in the "Students at risk of not meeting the course completion conditions" "table_row"
And following "Export" should download between "100" and "500" bytes
And following "Download" should download a "zip" file that:
| Contains | model-config.json |

Scenario: Check invalid site elements
When I open the action menu in "Students at risk of not meeting the course completion conditions" "table_row"
Expand Down
193 changes: 193 additions & 0 deletions lib/tests/behat/behat_download.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Steps definitions to verify a downloaded file.
*
* @package core
* @category test
* @copyright 2023 Simey Lameze <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

use Behat\Gherkin\Node\TableNode;
use Behat\Mink\Exception\ExpectationException;

require_once(__DIR__ . '/../../behat/behat_base.php');

/**
* Steps definitions to verify a downloaded file.
*
* @package core
* @category test
* @copyright 2023 Simey Lameze <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_download extends behat_base {

/**
* Downloads the file from a link on the page and checks the size is in a given range.
*
* @Then /^following "(?P<link_string>[^"]*)" should download a "(?P<format_string>[^"]*)" file that:$/
* | Contains | abc |
* | Does not contain | xyz |
*/
public function following_should_download_a_file_that(string $link, string $format, TableNode $table): void {

// Find the link.
$linknode = $this->find_link($link);
$this->ensure_node_is_visible($linknode);

// Get the href and check it.
$url = $linknode->getAttribute('href');
if (!$url) {
throw new ExpectationException(
'Download link does not have href attribute',
$this->getSession(),
);
}
if (!preg_match('~^https?://~', $url)) {
throw new ExpectationException(
"Download link not an absolute URL: {$url}",
$this->getSession(),
);
}

$behatgeneralcontext = behat_context_helper::get('behat_general');
$exception = new ExpectationException(
"Error while downloading data from {$link}",
$this->getSession(),
);

// It will stop spinning once file is downloaded or time out.
$filecontent = $this->spin(
fn($context, $link) => $behatgeneralcontext->download_file_from_link($link),
$link,
behat_base::get_extended_timeout(),
$exception
);

if (!$rows = $table->getRows()) {
// Nothing further to verify.
return;
}

// TODO:
// 2) Assert multiple files in the zip archive.
// 3) Validate txt, gift and png files.
// 4) Validate "Does not contain."
// 5) Validate "Contains" with multiple strings.
// 6) Validate "Does not contain PHP errors" -> (which can be based on look_for_exceptions from lib/behat/classes/behat_session_trait.php
$this->validate_mime_type($filecontent, $format);

$assertmethod = 'assert_file_type_' . strtolower($format);
foreach ($rows as $row) {
$assertion = strtolower(trim($row[0]));
match ($assertion) {
'contains' => $this->$assertmethod($filecontent, $row[1]),
default => throw new ExpectationException(
"Invalid assertion: {$assertion}",
$this->getSession(),
)
};
}
}

protected function validate_mime_type(string $filecontent, string $format): void {

$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimetype = $finfo->buffer($filecontent);

$validmimetypes = match ($format) {
'zip' => ['application/zip', 'application/x-zip-compressed', 'multipart/x-zip'],
'xml' => ['application/xml', 'text/xml'],
'txt' => ['text/plain'],
'gift' => ['text/plain'],
'aiken' => ['text/plain'],
'png' => ['image/png'],
default => [$format], // If the format is not one of the above, assume it is the only valid MIME type
};

if (!in_array($mimetype, $validmimetypes)) {
throw new ExpectationException(
"The file downloaded should have been a {$format} file, but got {$mimetype} instead.",
$this->getSession(),
);
}
}

protected function assert_file_type_xml(string $filecontent, string $contains): void {

// Load the XML content into a SimpleXMLElement object
$xml = new SimpleXMLElement($filecontent);

// Use xpath to search for the string in the XML content
$result = $xml->xpath("//*[contains(text(), '$contains')]");

// If the result is empty, the string was not found in the XML content
if (empty($result)) {
throw new ExpectationException(
"The string '{$contains}' was not found in the XML content.",
$this->getSession(),
);
}
}

protected function assert_file_type_zip(string $filecontent, string $expectedfile): void {

// Save the file to disk.
$tempdir = make_request_directory();
$filepath = $tempdir . '/downloaded.zip';
file_put_contents($filepath, $filecontent);

$zip = new ZipArchive();
$res = $zip->open($filepath);

if ($res !== true) {
throw new ExpectationException(
"Failed to open zip file.",
$this->getSession(),
);
}

// Check if the expected file exists in the zip archive.
if ($zip->locateName($expectedfile) === false) {
throw new ExpectationException(
"The file '{$expectedfile}' was not found in the zip archive.",
$this->getSession(),
);
}
}

protected function assert_file_type_text(string $filecontent, string $contains): void {

// Check if the string is present in the file content.
if (!str_contains($filecontent, $contains)) {
throw new ExpectationException(
"The string '{$contains}' was not found in the file content.",
$this->getSession(),
);
}
}

protected function assert_file_type_gift(string $filecontent, string $contains): void {
$this->assert_file_type_text($filecontent, $contains);
}

protected function assert_file_type_aiken(string $filecontent, string $contains): void {
$this->assert_file_type_text($filecontent, $contains);
}
}
2 changes: 2 additions & 0 deletions lib/tests/behat/behat_general.php
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,8 @@ function($context, $args) {
* @param number $maxexpectedsize the maximum expected file size in bytes.
*/
public function following_should_download_between_and_bytes($link, $minexpectedsize, $maxexpectedsize) {
// TO BE DEPRECATED

// If the minimum is greater than the maximum then swap the values.
if ((int)$minexpectedsize > (int)$maxexpectedsize) {
list($minexpectedsize, $maxexpectedsize) = array($maxexpectedsize, $minexpectedsize);
Expand Down
8 changes: 6 additions & 2 deletions mod/data/tests/behat/data_presets.feature
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,14 @@ Feature: Users can view and manage data presets
# The teacher should be able to export any saved preset.
And I open the action menu in "Saved preset by teacher1" "table_row"
Then I should see "Export"
And following "Export" "link" in the "Saved preset by teacher1" "table_row" should download between "1" and "5000" bytes
# Failing.
Then following "Export" should download a "xml" file that:
| Contains | Saved preset by teacher1 |
And I open the action menu in "Saved preset 1" "table_row"
And I should see "Export"
And following "Export" "link" in the "Saved preset 1" "table_row" should download between "1" and "5000" bytes
# Failing.
And following "Export" should download a "xml" file that:
| Contains | Saved preset 1 |

@javascript @_file_upload
Scenario Outline: Admins and Teachers can load a preset from a file
Expand Down
5 changes: 3 additions & 2 deletions mod/folder/tests/behat/recent_activity.feature
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ Feature: Files added in folder activity are visible in the recent activity block
And "//img[@alt='empty.txt']" "xpath_element" should exist
And "//img[contains(@src, 'preview=tinyicon')]" "xpath_element" should exist
# Confirm files are downloadable
And following "empty.txt" should download between "1" and "3000" bytes
And following "gd-logo.png" should download between "1" and "3000" bytes
Then following "Export" should download a file that:
| Is a valid txt file and contains | |
| Is a valid png file | |
3 changes: 2 additions & 1 deletion mod/glossary/tests/behat/create_entry.feature
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ Feature: Create a glossary entry.
When I am on the "Test glossary" "glossary activity" page logged in as teacher1
Then I should see "Entry 1"
And I should see "musicians.xml"
And following "musicians.xml" should download between "1" and "3000" bytes
And following "musicians.xml" should download a "xml" file that:
| Contains | Paul McCartney |
3 changes: 2 additions & 1 deletion question/format/aiken/tests/behat/aiken_export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Feature: Test exporting questions using Aiken format.
When I am on the "Course 1" "core_question > course question export" page logged in as "teacher1"
And I set the field "id_format_aiken" to "1"
When I press "Export questions to file"
Then following "click here" should download between "68" and "70" bytes
Then following "click here" should download a "aiken" file that:
| Contains | Which is the oddest number? |
# If the download step is the last in the scenario then we can sometimes run
# into the situation where the download page causes a http redirect but behat
# has already conducted its reset (generating an error). By putting a logout
Expand Down
3 changes: 2 additions & 1 deletion question/format/gift/tests/behat/import_export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ Feature: Test importing questions from GIFT format.
And I am on the "Course 1" "core_question > course question export" page
And I set the field "id_format_gift" to "1"
And I press "Export questions to file"
And following "click here" should download between "1500" and "1800" bytes
And following "click here" should download a "gift" file that:
| Contains | What's between orange and green in the spectrum? |

@javascript @_file_upload
Scenario: import a GIFT file which specifies the category
Expand Down
4 changes: 3 additions & 1 deletion question/format/xml/tests/behat/import_export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ Feature: Test importing questions from Moodle XML format.
And I set the field "id_format_xml" to "1"
And I set the field "Export category" to "TrueFalse"
And I press "Export questions to file"
Then following "click here" should download between "57100" and "58150" bytes
Then following "click here" should download a "xml" file that:
| Contains | Moodle acronym (False) |
| Contains | Moodle acronym (True) |

@javascript @_file_upload
Scenario: import some multiple choice questions from Moodle XML format
Expand Down
3 changes: 2 additions & 1 deletion question/type/ddimageortext/tests/behat/export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Feature: Test exporting drag and drop onto image questions
When I am on the "Course 1" "core_question > course question export" page logged in as teacher
And I set the field "id_format_xml" to "1"
And I press "Export questions to file"
Then following "click here" should download between "18600" and "19150" bytes
Then following "click here" should download a "xml" file that:
| Contains | Drag onto image |
# If the download step is the last in the scenario then we can sometimes run
# into the situation where the download page causes a http redirect but behat
# has already conducted its reset (generating an error). By putting a logout
Expand Down
3 changes: 2 additions & 1 deletion question/type/ddmarker/tests/behat/export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Feature: Test exporting drag and drop markers questions
When I am on the "Course 1" "core_question > course question export" page logged in as teacher
And I set the field "id_format_xml" to "1"
And I press "Export questions to file"
Then following "click here" should download between "233700" and "233950" bytes
Then following "click here" should download a "xml" file that:
| Contains | Drag markers |
# If the download step is the last in the scenario then we can sometimes run
# into the situation where the download page causes a http redirect but behat
# has already conducted its reset (generating an error). By putting a logout
Expand Down
3 changes: 2 additions & 1 deletion question/type/ddwtos/tests/behat/export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Feature: Test exporting drag and drop into text questions
When I am on the "Course 1" "core_question > course question export" page logged in as teacher
And I set the field "id_format_xml" to "1"
And I press "Export questions to file"
And following "click here" should download between "1550" and "1700" bytes
And following "click here" should download a "xml" file that:
| Contains | Drag to text |
# If the download step is the last in the scenario then we can sometimes run
# into the situation where the download page causes a http redirect but behat
# has already conducted its reset (generating an error). By putting a logout
Expand Down
Loading

0 comments on commit fe67ce3

Please sign in to comment.