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 ea6e0ad
Show file tree
Hide file tree
Showing 23 changed files with 254 additions and 29 deletions.
2 changes: 2 additions & 0 deletions admin/tool/admin_presets/tests/behat/behat_admin_presets.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class behat_admin_presets extends behat_base {
* @throws ExpectationException
*/
final public function following_in_the_should_download_between_and_bytes(string $link, string $selectortype,
// TO BE DEPRECATED.

string $nodeelement, string $nodeselectortype, int $minexpectedsize, int $maxexpectedsize): void {
// If the minimum is greater than the maximum then swap the values.
if ((int) $minexpectedsize > (int) $maxexpectedsize) {
Expand Down
7 changes: 5 additions & 2 deletions admin/tool/admin_presets/tests/behat/download.feature
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ Feature: I can download a preset
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.
Then following "Download" should download a 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 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 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 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 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 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 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 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 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
188 changes: 188 additions & 0 deletions lib/tests/behat/behat_download.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<?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'],
'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);
}
}
6 changes: 4 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,12 @@ 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
Then following "Export" should download a 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
And following "Export" should download a 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 file that:
| Contains | Musicians |
4 changes: 3 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,9 @@ 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 file that:
| Contains | Multi-choice-001 |
| Contains | Multi-choice-002 |
# 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 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 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 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 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
3 changes: 2 additions & 1 deletion question/type/description/tests/behat/export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Feature: Test exporting Description 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 "650" and "900" bytes
Then following "click here" should download a file that:
| Contains | description-001 |
# 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
5 changes: 4 additions & 1 deletion question/type/essay/tests/behat/export.feature
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ Feature: Test exporting Essay 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 "3000" and "3500" bytes
Then following "click here" should download a file that:
| Contains | essay-001 |
| Contains | essay-002 |
| Contains | essay-003 |
# 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 ea6e0ad

Please sign in to comment.