1
0
mirror of https://git.cloudron.io/cloudron/freshrss-app synced 2026-04-25 12:13:02 +00:00

test: port to charlie

Made-with: Cursor
This commit is contained in:
Girish Ramakrishnan
2026-04-24 20:37:49 +02:00
parent 654bc431c7
commit b08f62e4e2
+51 -144
View File
@@ -1,147 +1,78 @@
#!/usr/bin/env node #!/usr/bin/env node
import { execSync } from 'node:child_process';
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import superagent from '@cloudron/superagent'; import superagent from '@cloudron/superagent';
import { Builder, By, Key, until } from 'selenium-webdriver';
import { Options } from 'selenium-webdriver/chrome';
/* global it, xit, describe, before, after, afterEach */ import { app, clearCache, click, cloudronCli, goto, loginOIDC, password, sendKeys, setupBrowser, takeScreenshot, teardownBrowser, username, waitFor } from '@cloudron/charlie';
const admin_username = 'admin', admin_password = 'changeme'; /* global it, describe, before, after, afterEach */
if (!process.env.USERNAME || !process.env.PASSWORD) {
console.log('USERNAME and PASSWORD env vars need to be set');
process.exit(1);
}
describe('Application life cycle test', function () { describe('Application life cycle test', function () {
this.timeout(0); const admin_username = 'admin';
const admin_password = 'changeme';
const LOCATION = process.env.LOCATION || 'test'; before(setupBrowser);
const TEST_TIMEOUT = parseInt(process.env.TIMEOUT, 10) || 10000; after(teardownBrowser);
const EXEC_ARGS = { cwd: path.resolve(import.meta.dirname, '..'), stdio: 'inherit' };
const USERNAME = process.env.USERNAME;
const PASSWORD = process.env.PASSWORD;
let browser, app;
let athenticated_by_oidc = false;
before(function () {
const chromeOptions = new Options().windowSize({ width: 1280, height: 1024 });
if (process.env.CI) chromeOptions.addArguments('no-sandbox', 'disable-dev-shm-usage', 'headless');
browser = new Builder().forBrowser('chrome').setChromeOptions(chromeOptions).build();
if (!fs.existsSync('./screenshots')) fs.mkdirSync('./screenshots');
});
after(function () {
browser.quit();
});
afterEach(async function () { afterEach(async function () {
if (!process.env.CI || !app) return; await takeScreenshot(this.currentTest.title);
const currentUrl = await browser.getCurrentUrl();
if (!currentUrl.includes(app.domain)) return;
assert.strictEqual(typeof this.currentTest.title, 'string');
const screenshotData = await browser.takeScreenshot();
fs.writeFileSync(`./screenshots/${new Date().getTime()}-${this.currentTest.title.replaceAll(' ', '_')}.png`, screenshotData, 'base64');
}); });
function getAppInfo() {
var inspect = JSON.parse(execSync('cloudron inspect'));
app = inspect.apps.filter(function (a) { return a.location.indexOf(LOCATION) === 0; })[0];
assert.ok(app && typeof app === 'object');
}
function baseUrl() { function baseUrl() {
if (app.manifest.version === '1.4.0') return `https://${app.fqdn}/p`; if (app.manifest.version === '1.4.0') return `https://${app.fqdn}/p`;
return `https://${app.fqdn}`; return `https://${app.fqdn}`;
} }
async function waitForElement(elem) { async function login(uname, pass) {
await browser.wait(until.elementLocated(elem), TEST_TIMEOUT); await goto(`https://${app.fqdn}`, 'css=#loginButton');
await browser.wait(until.elementIsVisible(browser.findElement(elem)), TEST_TIMEOUT); await sendKeys('css=#username', uname);
await sendKeys('css=#passwordPlain', pass);
await click('css=#loginButton');
await waitFor('css=#btn-subscription');
} }
async function login(username, password) { async function loginViaOIDC() {
await browser.get('https://' + app.fqdn); await clearCache();
await waitForElement(By.id('loginButton')); await goto(`https://${app.fqdn}/i/`);
await browser.findElement(By.id('username')).sendKeys(username); await loginOIDC('css=#btn-subscription');
await browser.findElement(By.id('passwordPlain')).sendKeys(password);
await browser.findElement(By.id('loginButton')).click();
await waitForElement(By.id('btn-subscription'));
}
async function loginOIDC(username, password) {
browser.manage().deleteAllCookies();
await browser.get(`https://${app.fqdn}/i/`);
if (!athenticated_by_oidc) {
await waitForElement(By.id('inputUsername'));
await browser.findElement(By.id('inputUsername')).sendKeys(username);
await browser.findElement(By.id('inputPassword')).sendKeys(password);
await browser.findElement(By.id('loginSubmitButton')).click();
athenticated_by_oidc = true;
}
await waitForElement(By.id('btn-subscription'));
} }
async function logout() { async function logout() {
var logout_btn = By.xpath('//li/*[contains(@class,"signout")]'); await goto(`https://${app.fqdn}`, 'css=#stream');
await click('css=.dropdown-toggle');
await browser.get('https://' + app.fqdn); await click('xpath=//li/*[contains(@class,"signout")]');
await waitForElement(By.id('stream')); await waitFor('css=#loginButton');
await browser.findElement(By.className('dropdown-toggle')).click();
await waitForElement(logout_btn);
await browser.findElement(logout_btn).click();
await waitForElement(By.id('loginButton'));
} }
async function addSubscription() { async function addSubscription() {
var url = 'https://blog.cloudron.io/rss/'; const url = 'https://blog.cloudron.io/rss/';
const addUrl = app.manifest.version === '1.10.0' ? `${baseUrl()}/i/?c=subscription` : `${baseUrl()}/i/?c=subscription&a=add`; const addUrl = app.manifest.version === '1.10.0' ? `${baseUrl()}/i/?c=subscription` : `${baseUrl()}/i/?c=subscription&a=add`;
await browser.get(addUrl); await goto(addUrl, 'xpath=//input[@name="url_rss"]');
await waitForElement(By.xpath('//input[@name="url_rss"]')); await sendKeys('xpath=//input[@name="url_rss"]', url);
await browser.findElement(By.xpath('//input[@name="url_rss"]')).sendKeys(url); await click('xpath=//form[@id="add_rss"]//button[text()="Add"]');
await browser.findElement(By.xpath('//form[@id="add_rss"]//button[text()="Add"]')).click(); await waitFor('xpath=//div[@id="notification" and contains(@class, "good")]');
await waitForElement(By.xpath('//div[@id="notification" and contains(@class, "good")]'));
} }
async function enableApi(relogin = true) { async function enableApi(relogin = true) {
await browser.get(`${baseUrl()}/i/?c=auth`); await goto(`${baseUrl()}/i/?c=auth`);
if (relogin) { if (relogin) {
// needs a relogin for admin confirmation await sendKeys('css=#passwordPlain', admin_password);
await waitForElement(By.id('passwordPlain')); await click('xpath=//button[contains(.,"Login")]');
await browser.findElement(By.id('passwordPlain')).sendKeys(admin_password);
await browser.sleep(3000);
console.log('Clicking the login button');
await browser.findElement(By.xpath('//button[contains(.,"Login")]')).click();
} }
await waitForElement(By.id('api_enabled')); await click('css=#api_enabled');
await browser.findElement(By.id('api_enabled')).click(); await click('xpath=//button[text()="Submit"]');
await browser.findElement(By.xpath('//button[text()="Submit"]')).click();
await browser.sleep(5000);
} }
async function checkApiConfiguration() { async function checkApiConfiguration() {
await browser.get(`${baseUrl()}/api/`); await goto(`${baseUrl()}/api/`, 'xpath=//dd[@id="greaderOutput" and contains(text(), "PASS")]');
await waitForElement(By.xpath('//dd[@id="greaderOutput" and contains(text(), "PASS")]')); await waitFor('xpath=//dd[@id="feverOutput" and contains(text(), "PASS")]');
await waitForElement(By.xpath('//dd[@id="feverOutput" and contains(text(), "PASS")]'));
} }
async function subscriptionExists() { async function subscriptionExists() {
await browser.get(`${baseUrl()}/i/?get=c_1`); await goto(`${baseUrl()}/i/?get=c_1`, 'Cloudron');
return waitForElement(By.xpath('//span[text()="Cloudron"]'));
} }
async function getStaticExtensionFile() { async function getStaticExtensionFile() {
@@ -149,14 +80,11 @@ describe('Application life cycle test', function () {
.buffer(true) .buffer(true)
.ok(() => true); .ok(() => true);
assert.strictEqual(response.status, 200); assert.strictEqual(response.status, 200);
assert.ok(response.text.includes('sticky_feeds')); // relies on the buffer flag above assert.ok(response.text.includes('sticky_feeds'));
} }
xit('build app', function () { execSync('cloudron build', EXEC_ARGS); });
// No SSO // No SSO
it('install app (no sso)', function () { execSync('cloudron install --no-sso --location ' + LOCATION, EXEC_ARGS); }); it('install app (no sso)', () => cloudronCli.install({ noSso: true }));
it('can get app information', getAppInfo);
it('can login', login.bind(null, admin_username, admin_password)); it('can login', login.bind(null, admin_username, admin_password));
it('can subscribe', addSubscription); it('can subscribe', addSubscription);
@@ -165,68 +93,47 @@ describe('Application life cycle test', function () {
it('subscription exists', subscriptionExists); it('subscription exists', subscriptionExists);
it('can get static extension file', getStaticExtensionFile); it('can get static extension file', getStaticExtensionFile);
it('can logout', logout); it('can logout', logout);
it('uninstall app', function () { execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS); }); it('uninstall app', cloudronCli.uninstall);
// SSO // SSO
it('install app (sso)', function () { execSync('cloudron install --location ' + LOCATION, EXEC_ARGS); }); it('install app (sso)', cloudronCli.install);
it('can get app information', getAppInfo); it('can make user Administrator', () => cloudronCli.exec(`bash -c "php cli/reconfigure.php --default-user ${username}"`));
it('can login OIDC', () => loginViaOIDC(username, password));
it('can make user Administrator', function () { execSync(`cloudron exec --app ${app.id} -- bash -c "php cli/reconfigure.php --default-user ${USERNAME}"`); });
it('can login OIDC', loginOIDC.bind(null, USERNAME, PASSWORD));
it('can subscribe', addSubscription); it('can subscribe', addSubscription);
it('can enable API', enableApi.bind(null, false /* relogin */)); it('can enable API', enableApi.bind(null, false /* relogin */));
it('can check configuration', checkApiConfiguration); it('can check configuration', checkApiConfiguration);
it('subscription exists', subscriptionExists); it('subscription exists', subscriptionExists);
it('can get static extension file', getStaticExtensionFile); it('can get static extension file', getStaticExtensionFile);
it('backup app', function () { execSync('cloudron backup create --app ' + app.id, EXEC_ARGS); }); it('backup app', cloudronCli.createBackup);
it('restore app', cloudronCli.restoreFromLatestBackup);
it('restore app', function () { it('can login OIDC', loginViaOIDC);
const backups = JSON.parse(execSync(`cloudron backup list --raw --app ${app.id}`));
execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS);
execSync('cloudron install --location ' + LOCATION, EXEC_ARGS);
getAppInfo();
execSync(`cloudron restore --backup ${backups[0].id} --app ${app.id}`, EXEC_ARGS);
});
it('can login OIDC', loginOIDC.bind(null, USERNAME, PASSWORD));
it('can check configuration', checkApiConfiguration); it('can check configuration', checkApiConfiguration);
it('subscription exists', subscriptionExists); it('subscription exists', subscriptionExists);
it('can get static extension file', getStaticExtensionFile); it('can get static extension file', getStaticExtensionFile);
it('move to different location', function () { it('move to different location', cloudronCli.changeLocation);
browser.manage().deleteAllCookies();
execSync('cloudron configure --location ' + LOCATION + '2 --app ' + app.id, EXEC_ARGS);
var inspect = JSON.parse(execSync('cloudron inspect'));
app = inspect.apps.filter(function (a) { return a.location === LOCATION + '2'; })[0];
assert.ok(app && typeof app === 'object');
});
it('can login OIDC', loginOIDC.bind(null, USERNAME, PASSWORD)); it('can login OIDC', loginViaOIDC);
it('can check configuration', checkApiConfiguration); it('can check configuration', checkApiConfiguration);
it('subscription exists', subscriptionExists); it('subscription exists', subscriptionExists);
it('can get static extension file', getStaticExtensionFile); it('can get static extension file', getStaticExtensionFile);
it('uninstall app', function () { execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS); }); it('uninstall app', cloudronCli.uninstall);
// test update // test update
it('can install app for update', function () { execSync('cloudron install --appstore-id org.freshrss.cloudronapp --location ' + LOCATION, EXEC_ARGS); }); it('can install app for update', cloudronCli.appstoreInstall);
it('can get app information', getAppInfo); it('can login OIDC', loginViaOIDC);
it('can login OIDC', loginOIDC.bind(null, USERNAME, PASSWORD)); it('can make user Administrator', () => cloudronCli.exec(`bash -c "php cli/reconfigure.php --default-user ${username}"`));
it('can make user Administrator', function () { execSync(`cloudron exec --app ${app.id} -- bash -c "php cli/reconfigure.php --default-user ${USERNAME}"`); });
it('can subscribe', addSubscription); it('can subscribe', addSubscription);
it('can update', function () { it('can update', cloudronCli.update);
execSync('cloudron update --app ' + app.id, EXEC_ARGS);
var inspect = JSON.parse(execSync('cloudron inspect'));
app = inspect.apps.filter(function (a) { return a.location === LOCATION; })[0];
assert.ok(app && typeof app === 'object');
});
it('subscription exists', subscriptionExists); it('subscription exists', subscriptionExists);
it('can get static extension file', getStaticExtensionFile); it('can get static extension file', getStaticExtensionFile);
it('uninstall app', function () { execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS); }); it('uninstall app', cloudronCli.uninstall);
}); });