From 0662f569ea969663308fce2a1a07fe1cbafa8e41 Mon Sep 17 00:00:00 2001 From: Christian Heel <66922325+heelc29@users.noreply.github.com> Date: Wed, 30 Oct 2024 08:13:32 +0100 Subject: [PATCH] [5.2] add test for site router (sef) (#44253) * [5.2] add test for site router (sef) * use copyFileSync --- tests/System/README.md | 1 + tests/System/drone-system-run.sh | 1 + .../plugins/system/sef/SefPlugin.cy.js | 115 +++++++++++++++ .../site/components/com_contact/Router.cy.js | 132 ++++++++++++++++++ .../site/components/com_content/Router.cy.js | 130 +++++++++++++++++ tests/System/plugins/fs.mjs | 24 +++- tests/System/plugins/index.mjs | 3 +- 7 files changed, 403 insertions(+), 3 deletions(-) create mode 100644 tests/System/integration/plugins/system/sef/SefPlugin.cy.js create mode 100644 tests/System/integration/site/components/com_contact/Router.cy.js create mode 100644 tests/System/integration/site/components/com_content/Router.cy.js diff --git a/tests/System/README.md b/tests/System/README.md index a009c0d488536..7d880847001e4 100644 --- a/tests/System/README.md +++ b/tests/System/README.md @@ -163,6 +163,7 @@ The Joomla System Tests come with some convenient [Cypress Tasks](https://docs.c - **cleanupDB** – Deletes the inserted items from the database - **writeRelativeFile** – Writes a file relative to the CMS root folder - **deleteRelativePath** – Deletes a file or folder relative to the CMS root folder +- **copyRelativeFile** – Copies a file relative to the CMS root folder - **startMailServer** – Starts the smtp-tester SMTP server - **getMails** – Get received mails from smtp-tester - **clearEmails** – Clear all smtp-tester received mails diff --git a/tests/System/drone-system-run.sh b/tests/System/drone-system-run.sh index 7b106355bc879..823b8f08c598e 100644 --- a/tests/System/drone-system-run.sh +++ b/tests/System/drone-system-run.sh @@ -18,6 +18,7 @@ chown -R www-data /tests/www/$TEST_GROUP/ chmod -R 777 /tests/www/$TEST_GROUP/images echo "[RUNNER] Start Apache" +a2enmod rewrite apache2ctl -D FOREGROUND & echo "[RUNNER] Run cypress tests" diff --git a/tests/System/integration/plugins/system/sef/SefPlugin.cy.js b/tests/System/integration/plugins/system/sef/SefPlugin.cy.js new file mode 100644 index 0000000000000..35b27448284b0 --- /dev/null +++ b/tests/System/integration/plugins/system/sef/SefPlugin.cy.js @@ -0,0 +1,115 @@ +describe('Test that the sef system plugin', () => { + afterEach(() => { + cy.task('deleteRelativePath', '.htaccess'); + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef=true sef_suffix=false sef_rewrite=false`); + cy.db_updateExtensionParameter('enforcesuffix', '1', 'plg_system_sef'); + cy.db_updateExtensionParameter('indexphp', '1', 'plg_system_sef'); + cy.db_updateExtensionParameter('trailingslash', '0', 'plg_system_sef'); + cy.db_updateExtensionParameter('strictrouting', '1', 'plg_system_sef'); + }); + + it('can process if option \'sef\' disabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef=false`); + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login', failOnStatusCode: false, followRedirect: false }).then((response) => { + expect(response.status).to.eq(404); + }); + }); + + it('can process if option \'enforcesuffix\' enabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_suffix=true`); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\.html$/); + }); + cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'enforcesuffix\' disabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_suffix=true`); + cy.db_updateExtensionParameter('enforcesuffix', '0', 'plg_system_sef'); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'indexphp\' enabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_rewrite=true`); + cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/(? { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'indexphp\' disabled', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_rewrite=true`); + cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' }); + cy.db_updateExtensionParameter('indexphp', '0', 'plg_system_sef'); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'trailingslash\' disabled', () => { + cy.request({ url: '/index.php/component/users/login/', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.visit('/'); + cy.get('li.nav-item').contains('Home') + .should('have.attr', 'href') + .and('match', /\/index\.php$/); + }); + + it('can process if option \'trailingslash\' enabled', () => { + cy.db_updateExtensionParameter('trailingslash', '1', 'plg_system_sef'); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\/$/); + }); + cy.request({ url: '/index.php/component/users/login/', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.visit('/'); + cy.get('li.nav-item').contains('Home') + .should('have.attr', 'href') + .and('match', /\/index\.php\/$/); + }); + + it('can process if option \'strictrouting\' enabled', () => { + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); + + it('can process if option \'strictrouting\' disabled', () => { + cy.db_updateExtensionParameter('strictrouting', '0', 'plg_system_sef'); + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + }); + }); +}); diff --git a/tests/System/integration/site/components/com_contact/Router.cy.js b/tests/System/integration/site/components/com_contact/Router.cy.js new file mode 100644 index 0000000000000..90ac76acfeb89 --- /dev/null +++ b/tests/System/integration/site/components/com_contact/Router.cy.js @@ -0,0 +1,132 @@ +describe('Test in frontend that the contact site router', () => { + it('can process contact without a menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/contact\/contact\/test-contact-router$/); + }); + + cy.visit('/index.php/component/contact/contact/test-contact-router'); + cy.url().should('match', /\/index\.php\/component\/contact\/contact\/test-contact-router$/); + cy.title().should('equal', 'Test Contact'); + cy.get('main h1').contains('Home'); + cy.get('main h2').contains('Test Contact'); + cy.get('main h3').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Contact'); + }); + }); + + it('can process contact with a single contact menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.db_createMenuItem({ + title: 'Test Menu Single Contact', + alias: 'test-menu-contact-router', + path: 'test-menu-contact-router', + link: `index.php?option=com_contact&view=contact&id=${contact.id}`, + }); + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}&catid=${contact.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-contact-router$/); + }); + + cy.visit('/index.php/test-menu-contact-router'); + cy.url().should('match', /\/index\.php\/test-menu-contact-router$/); + cy.title().should('equal', 'Test Menu Single Contact'); + cy.get('main h1').contains('Test Contact'); + cy.get('main h2').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Single Contact'); + }); + }); + + it('can process contact with a category list menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.db_createMenuItem({ + title: 'Test Menu Contact Category', + alias: 'test-menu-category-router', + path: 'test-menu-category-router', + link: `index.php?option=com_contact&view=category&id=${contact.catid}`, + }); + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}&catid=${contact.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-category-router\/test-contact-router$/); + }); + + cy.visit('/index.php/test-menu-category-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router$/); + cy.title().should('equal', 'Test Menu Contact Category'); + cy.get('main h1').contains('Uncategorised'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Category'); + cy.get('main div.com-contact-category a') + .contains('Test Contact') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-category-router\/test-contact-router$/); + + cy.visit('/index.php/test-menu-category-router/test-contact-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router\/test-contact-router$/); + cy.title().should('equal', 'Test Contact'); + cy.get('main h1').contains('Test Contact'); + cy.get('main h2').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Category'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Contact'); + }); + }); + + it('can process contact with a categories list menu item', () => { + cy.db_createContact({ name: 'Test Contact', alias: 'test-contact-router' }).then((contact) => { + cy.db_createMenuItem({ + title: 'Test Menu Contact Categories', + alias: 'test-menu-categories-router', + path: 'test-menu-categories-router', + link: 'index.php?option=com_contact&view=categories&id=0', + }); + cy.request({ url: `/index.php?option=com_contact&view=contact&id=${contact.id}&catid=${contact.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-categories-router\/uncategorised\/test-contact-router$/); + }); + + cy.visit('/index.php/test-menu-categories-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router$/); + cy.title().should('equal', 'Test Menu Contact Categories'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Categories'); + cy.get('main div.com-contact-categories h3 a') + .contains('Uncategorised') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + cy.title().should('equal', 'Test Menu Contact Categories'); + cy.get('main h1').contains('Uncategorised'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('main div.com-contact-category a') + .contains('Test Contact') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-contact-router$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised/test-contact-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-contact-router$/); + cy.title().should('equal', 'Test Contact'); + cy.get('main h1').contains('Test Contact'); + cy.get('main h2').contains('Contact'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 5); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Contact Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(4).should('contain', 'Test Contact'); + }); + }); +}); diff --git a/tests/System/integration/site/components/com_content/Router.cy.js b/tests/System/integration/site/components/com_content/Router.cy.js new file mode 100644 index 0000000000000..0036e3d196fb7 --- /dev/null +++ b/tests/System/integration/site/components/com_content/Router.cy.js @@ -0,0 +1,130 @@ +describe('Test in frontend that the content site router', () => { + it('can process article without a menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(200); + // @TODO: Not working if 'Featured Articles' is home menu item + // expect(response.status).to.eq(301); + // expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/content\/article\/test-content-router$/); + }); + + cy.visit('/index.php/component/content/article/test-content-router'); + cy.url().should('match', /\/index\.php\/component\/content\/article\/test-content-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Home'); + cy.get('main h2').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Article'); + }); + }); + + it('can process article with a single article menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.db_createMenuItem({ + title: 'Test Menu Single Article', + alias: 'test-menu-article-router', + path: 'test-menu-article-router', + link: `index.php?option=com_content&view=article&id=${article.id}`, + }); + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}&catid=${article.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-article-router$/); + }); + + cy.visit('/index.php/test-menu-article-router'); + cy.url().should('match', /\/index\.php\/test-menu-article-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Single Article'); + }); + }); + + it('can process article with a category list menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.db_createMenuItem({ + title: 'Test Menu Article Category', + alias: 'test-menu-category-router', + path: 'test-menu-category-router', + link: `index.php?option=com_content&view=category&id=${article.catid}`, + }); + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}&catid=${article.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-category-router\/test-content-router$/); + }); + + cy.visit('/index.php/test-menu-category-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router$/); + cy.title().should('equal', 'Test Menu Article Category'); + cy.get('main h1').should('not.exist'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Category'); + cy.get('main div.com-content-category a') + .contains('Test Article') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-category-router\/test-content-router$/); + + cy.visit('/index.php/test-menu-category-router/test-content-router'); + cy.url().should('match', /\/index\.php\/test-menu-category-router\/test-content-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Category'); + cy.get('@breadcrumb').eq(3).should('contain', 'Test Article'); + }); + }); + + it('can process article with a categories list menu item', () => { + cy.db_createArticle({ title: 'Test Article', alias: 'test-content-router' }).then((article) => { + cy.db_createMenuItem({ + title: 'Test Menu Article Categories', + alias: 'test-menu-categories-router', + path: 'test-menu-categories-router', + link: 'index.php?option=com_content&view=categories&id=0', + }); + cy.request({ url: `/index.php?option=com_content&view=article&id=${article.id}&catid=${article.catid}`, followRedirect: false }).then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/test-menu-categories-router\/uncategorised\/test-content-router$/); + }); + + cy.visit('/index.php/test-menu-categories-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router$/); + cy.title().should('equal', 'Test Menu Article Categories'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 3); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Categories'); + cy.get('main div.com-content-categories div a') + .contains('Uncategorised') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised$/); + cy.title().should('equal', 'Uncategorised'); + cy.get('main h1').should('not.exist'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 4); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('main div.com-content-category-blog h2 a') + .contains('Test Article') + .should('have.attr', 'href') + .and('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-content-router$/); + + cy.visit('/index.php/test-menu-categories-router/uncategorised/test-content-router'); + cy.url().should('match', /\/index\.php\/test-menu-categories-router\/uncategorised\/test-content-router$/); + cy.title().should('equal', 'Test Article'); + cy.get('main h1').contains('Test Article'); + cy.get('nav.mod-breadcrumbs__wrapper ol.mod-breadcrumbs').children().as('breadcrumb'); + cy.get('@breadcrumb').should('have.length', 5); + cy.get('@breadcrumb').eq(2).should('contain', 'Test Menu Article Categories'); + cy.get('@breadcrumb').eq(3).should('contain', 'Uncategorised'); + cy.get('@breadcrumb').eq(4).should('contain', 'Test Article'); + }); + }); +}); diff --git a/tests/System/plugins/fs.mjs b/tests/System/plugins/fs.mjs index ae4d35c8da78f..0ce0b7a1c63ca 100644 --- a/tests/System/plugins/fs.mjs +++ b/tests/System/plugins/fs.mjs @@ -1,5 +1,5 @@ import { - chmodSync, existsSync, writeFileSync, mkdirSync, rmSync, + chmodSync, existsSync, writeFileSync, mkdirSync, rmSync, copyFileSync } from 'fs'; import { dirname, join } from 'path'; import { umask } from 'node:process'; @@ -56,4 +56,24 @@ function writeRelativeFile(relativePath, content, config, mode = 0o444) { return null; } -export { writeRelativeFile, deleteRelativePath }; +/** + * Copies a file to a specified path relative to the CMS root folder. + * + * If the file already exists, it will be overwritten. + * + * @param {string} source - The relative file path of the existing file + * @param {string} destination - The relative file path of the new file + * @param {object} config - The Cypress configuration object + * + * @returns null + */ +function copyRelativeFile(source, destination, config) { + const fullSource = join(config.env.cmsPath, source); + const fullDestination = join(config.env.cmsPath, destination); + + copyFileSync(fullSource, fullDestination); + + return null; +} + +export { writeRelativeFile, deleteRelativePath, copyRelativeFile }; diff --git a/tests/System/plugins/index.mjs b/tests/System/plugins/index.mjs index ab7c8a2b72a5e..0c36b2f6940c3 100644 --- a/tests/System/plugins/index.mjs +++ b/tests/System/plugins/index.mjs @@ -1,5 +1,5 @@ import { getMails, clearEmails, startMailServer } from './mail.mjs'; -import { writeRelativeFile, deleteRelativePath } from './fs.mjs'; +import { writeRelativeFile, deleteRelativePath, copyRelativeFile } from './fs.mjs'; import { queryTestDB, deleteInsertedItems } from './db.mjs'; /** @@ -16,6 +16,7 @@ export default function setupPlugins(on, config) { cleanupDB: () => deleteInsertedItems(config), writeRelativeFile: ({ path, content, mode }) => writeRelativeFile(path, content, config, mode), deleteRelativePath: (path) => deleteRelativePath(path, config), + copyRelativeFile: ({ source, destination }) => copyRelativeFile(source, destination, config), getMails: () => getMails(), clearEmails: () => clearEmails(), startMailServer: () => startMailServer(config),