Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions e2e/diag-phase5-smoke.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Phase 5 smoke test: verify Tabler layout, BS5 modal shim, and core navigation
// Tests: login, dashboard loads, modal opens/closes, case page loads, no JS errors
const { chromium } = require('@playwright/test');
const fs = require('node:fs');
const dotenv = require('dotenv');

const BASE = process.env.BASE_URL || 'https://127.0.0.1:8443';
const env = dotenv.parse(fs.readFileSync('../.env'));
const PASS = env.IRIS_ADM_PASSWORD;

let errors = 0;
function ok(label) { console.log(` ✓ ${label}`); }
function fail(label, msg) { console.error(` ✗ ${label}: ${msg}`); errors++; }

(async () => {
const browser = await chromium.launch({ args: ['--ignore-certificate-errors'] });
const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await ctx.newPage();

const jsErrors = [];
page.on('pageerror', e => jsErrors.push(e.message));

try {
// 1. Login
console.log('\n[1] Login');
await page.goto(BASE + '/');
await page.getByRole('textbox', { name: 'Username' }).fill('administrator');
await page.getByRole('textbox', { name: 'Password' }).fill(PASS);
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('**/dashboard**', { timeout: 15000 });
ok('login and redirect to dashboard');

// 2. Tabler layout loaded (check for .page container from 5.1)
console.log('\n[2] Tabler layout');
const pageDiv = await page.$('div.page');
pageDiv ? ok('div.page container present (Tabler)') : fail('Tabler container', 'div.page not found — Atlantis .wrapper still in DOM?');

// Check tabler.min.css loaded (check for a Tabler-specific CSS class presence)
const tablerLink = await page.$('link[href*="tabler.min.css"]');
tablerLink ? ok('tabler.min.css link present') : fail('tabler.min.css', 'link tag not found in head');

// Check Atlantis CSS is gone
const atlantisLink = await page.$('link[href*="atlantis.css"]');
!atlantisLink ? ok('atlantis.css removed') : fail('atlantis.css', 'still loaded — not removed from layout');

// 3. JS errors so far
console.log('\n[3] JS errors after login');
const criticalErrors = jsErrors.filter(e =>
e.includes('TypeError') || e.includes('is not a function') || e.includes('is not defined')
);
criticalErrors.length === 0
? ok('no TypeError/undefined JS errors on login+dashboard')
: fail('JS errors', criticalErrors.slice(0, 3).join(' | '));
jsErrors.length = 0;

// 4. Navigate to case 1 and test modal shim
console.log('\n[4] Case page + modal shim');
await page.goto(BASE + '/case?cid=1', { timeout: 15000 });
await page.waitForLoadState('networkidle', { timeout: 15000 });
ok('case page loaded');

// Trigger a modal via the BS5 shim — click "Add task" button
const addTaskBtn = page.getByRole('button', { name: 'Add task' });
if (await addTaskBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
await addTaskBtn.click();
// Modal heading should appear
const heading = page.getByRole('heading', { name: 'Add task' });
const visible = await heading.isVisible({ timeout: 5000 }).catch(() => false);
visible ? ok('modal opens via BS5 shim (Add task)') : fail('modal shim', 'Add task modal heading not visible after click');

// Close modal
const closeBtn = page.locator('.modal.show .btn-close').first();
if (await closeBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await closeBtn.click();
const gone = await heading.isHidden({ timeout: 5000 }).catch(() => false);
gone ? ok('modal closes via btn-close (data-bs-dismiss)') : fail('modal close', 'modal still visible after btn-close click');
}
} else {
console.log(' ~ Add task button not found on case page (case may not have tasks tab visible)');
}

// 5. Case notes page (ext layout with Milkdown)
console.log('\n[5] Case notes ext layout');
await page.goto(BASE + '/case/notes?cid=1', { timeout: 15000 });
await page.waitForLoadState('networkidle', { timeout: 15000 });
const extPageDiv = await page.$('div.page');
extPageDiv ? ok('default_ext layout: div.page container present') : fail('ext layout', 'div.page not found on notes page');

// 6. JS errors on case pages
console.log('\n[6] JS errors on case pages');
const caseErrors = jsErrors.filter(e =>
e.includes('TypeError') || e.includes('is not a function') || e.includes('is not defined')
);
caseErrors.length === 0
? ok('no critical JS errors on case pages')
: fail('JS errors on case', caseErrors.slice(0, 3).join(' | '));

// 7. Manage page (checks data-bs-toggle in navigation)
console.log('\n[7] Manage page');
await page.goto(BASE + '/manage/cases', { timeout: 15000 });
await page.waitForLoadState('networkidle', { timeout: 10000 });
ok('manage/cases page loaded');

} catch (e) {
fail('unexpected error', e.message);
} finally {
await browser.close();
}

console.log(`\n${'─'.repeat(50)}`);
if (errors === 0) {
console.log('Phase 5 smoke: ALL PASS');
} else {
console.log(`Phase 5 smoke: ${errors} FAILURE(S) — see above`);
process.exit(1);
}
})();
2 changes: 1 addition & 1 deletion e2e/playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ module.exports = defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'https://localhost',
baseURL: process.env.BASE_URL || 'https://localhost',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
Expand Down
10 changes: 7 additions & 3 deletions source/app/blueprints/pages/dashboard/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

{% block stylesheets %}
<link rel="stylesheet" href="/static/assets/css/suggestags.css">
<link href="/static/assets/css/dataTables.bootstrap5.min.css" rel="stylesheet">
<link href="/static/assets/css/dataTables.contextualActions.min.css" rel="stylesheet">
<link href="/static/assets/css/dataTables.select.min.css" rel="stylesheet">
<link href="/static/assets/css/select.bootstrap5.min.css" rel="stylesheet">
{% endblock stylesheets %}

{% block content %}
Expand Down Expand Up @@ -39,7 +40,7 @@ <h2 class="text-white pb-2 fw-bold">Dashboard</h2>
<div class="row">
<div class="col-5">
<div class="icon-big text-center">
<i class="flaticon-file-1 text-success"></i>
<i class="fas fa-file-alt text-success"></i>
</div>
</div>
<div class="col-7 col-stats">
Expand All @@ -58,7 +59,7 @@ <h4 class="card-title">{{ data.cases_open_count }} / {{ data.cases_count }}</h4
<div class="row">
<div class="col-5">
<div class="icon-big text-center">
<i class="flaticon-suitcase text-warning"></i>
<i class="fas fa-briefcase text-warning"></i>
</div>
</div>
<div class="col-7 col-stats">
Expand Down Expand Up @@ -284,6 +285,9 @@ <h4 class="card-title" id="user_attr_count"></h4>
<script src="/static/assets/js/plugin/datatables/dataTables.select.min.js"></script>
<script src="/static/assets/js/plugin/datatables/buttons.html5.min.js"></script>
<script src="/static/assets/js/plugin/datatables/buttons.print.min.js"></script>
<script src="/static/assets/js/plugin/datatables-bs5/dataTables.bootstrap5.min.js"></script>
<script src="/static/assets/js/plugin/datatables-bs5/buttons.bootstrap5.min.js"></script>
<script src="/static/assets/js/plugin/datatables-bs5/select.bootstrap5.min.js"></script>
<script src="/static/assets/js/iris/dashboard.js"></script>
<script src="/static/assets/js/core/charts.js"></script>

Expand Down
32 changes: 16 additions & 16 deletions source/app/templates/includes/datastore-sidebar.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<div class="ds-sidebar" id="ds-sidebar-container">
<a href="#" class="close-ds-sidebar" onclick="hide_datastore();">
<i class="flaticon-cross"></i>
<i class="fas fa-times"></i><!-- TODO: icon review flaticon-cross -->
</a>
<div class="ds-sidebar-wrapper">
<ul class="nav nav-tabs nav-line nav-color-primary" role="tablist">
<li class="nav-item"> <a class="nav-link active show" data-toggle="tab" href="#ds" role="tab" aria-controls="case" aria-selected="true">Datastore</a> </li>
<li class="nav-item"> <a class="nav-link active show" data-bs-toggle="tab" href="#ds" role="tab" aria-controls="case" aria-selected="true">Datastore</a> </li>
</ul>
<div class="tab-content mt-3">
<div class="tab-pane fade show active" id="ds" role="tabpanel">
Expand All @@ -13,36 +13,36 @@
</div>
<div class="tree well" id="msg_select_destination_folder" style="display:none;">
<div class="row">
<p class="mt-3 ml-3">Select the files and destination folder below. <span id="msg_mv_files" class="text-primary"></span> files selected to be moved to <span id="msg_mv_dst_folder" class="text-primary"></span></p>
<button type="button" class="btn btn-sm btn-outline-success ml-auto mr-2" onclick="validate_ds_file_move();">Validate</button>
<button type="button" class="btn btn-sm btn-outline-dark mr-4" onclick="reset_ds_file_view();load_datastore();">Cancel</button>
<p class="mt-3 ms-3">Select the files and destination folder below. <span id="msg_mv_files" class="text-primary"></span> files selected to be moved to <span id="msg_mv_dst_folder" class="text-primary"></span></p>
<button type="button" class="btn btn-sm btn-outline-success ms-auto me-2" onclick="validate_ds_file_move();">Validate</button>
<button type="button" class="btn btn-sm btn-outline-dark me-4" onclick="reset_ds_file_view();load_datastore();">Cancel</button>
</div>
</div>
<div class="tree well" id="msg_select_destination_folder_folder" style="display:none;">
<div class="row">
<p class="mt-3 ml-3">Select a destination folder below. <span id="msg_mv_folder" class="text-primary"></span> selected to be moved to <span id="msg_mv_dst_folder_folder" class="text-primary"></span></p>
<button type="button" class="btn btn-sm btn-outline-success ml-auto mr-2" onclick="validate_ds_folder_move();">Validate</button>
<button type="button" class="btn btn-sm btn-outline-dark mr-4" onclick="reset_ds_file_view();load_datastore();">Cancel</button>
<p class="mt-3 ms-3">Select a destination folder below. <span id="msg_mv_folder" class="text-primary"></span> selected to be moved to <span id="msg_mv_dst_folder_folder" class="text-primary"></span></p>
<button type="button" class="btn btn-sm btn-outline-success ms-auto me-2" onclick="validate_ds_folder_move();">Validate</button>
<button type="button" class="btn btn-sm btn-outline-dark me-4" onclick="reset_ds_file_view();load_datastore();">Cancel</button>
</div>
</div>
<div class="row mb-3">
<div class="col-9">
<div class="row">
<div id='ds_file_search' class="ml-3 pt-2 pl-2" style="width:70%;border-radius:3px;border-color:#fff;background:rgb(234, 234, 234);" ></div>
<button class="btn btn-sm btn-light ml-2 pt-2" onclick="filter_ds_files();" id="btn_filter_ds_files">
<div id='ds_file_search' class="ms-3 pt-2 ps-2" style="width:70%;border-radius:3px;border-color:#fff;background:rgb(234, 234, 234);" ></div>
<button class="btn btn-sm btn-light ms-2 pt-2" onclick="filter_ds_files();" id="btn_filter_ds_files">
Search
</button>
<button class="btn btn-sm btn-light ml-2 pt-2" onclick="reset_ds_files_filter();">
<button class="btn btn-sm btn-light ms-2 pt-2" onclick="reset_ds_files_filter();">
Reset
</button>
<div class="ml-1 mt-1 fa-regular text-dark fa-circle-question" title="Filter help" onclick="show_ds_filter_help();"></div>
<div class="ms-1 mt-1 fa-regular text-dark fa-circle-question" title="Filter help" onclick="show_ds_filter_help();"></div>
</div>
</div>
<div class="col-3 justify-content-end">
<button type="button" class="btn btn-sm btn-outline-dark float-right mb-1 mr-3 btn-ds-bulk-selector" onclick="toggle_select_file();"><i class="fa-solid fa-check-double mr-1"></i> Select</button>
<button type="button" class="btn btn-sm btn-outline-dark float-right mb-1 mr-3" onclick="refresh_ds();"><i class="fa-solid fa-arrows-rotate"></i> Refresh</button>
<button type="button" class="btn btn-sm btn-outline-danger float-right mt-1 mr-3 btn-ds-bulk" style="display:none;" onclick="delete_bulk_ds_file();"><i class="fa fa-trash text-danger mr-1"></i> Delete</button>
<button type="button" class="btn btn-sm btn-outline-dark float-right mt-1 mr-3 btn-ds-bulk" style="display:none;" onclick="move_ds_file();"><i class="fa-solid fa-arrow-right-arrow-left mr-1"></i> Move</button>
<button type="button" class="btn btn-sm btn-outline-dark float-end mb-1 me-3 btn-ds-bulk-selector" onclick="toggle_select_file();"><i class="fa-solid fa-check-double me-1"></i> Select</button>
<button type="button" class="btn btn-sm btn-outline-dark float-end mb-1 me-3" onclick="refresh_ds();"><i class="fa-solid fa-arrows-rotate"></i> Refresh</button>
<button type="button" class="btn btn-sm btn-outline-danger float-end mt-1 me-3 btn-ds-bulk" style="display:none;" onclick="delete_bulk_ds_file();"><i class="fa fa-trash text-danger me-1"></i> Delete</button>
<button type="button" class="btn btn-sm btn-outline-dark float-end mt-1 me-3 btn-ds-bulk" style="display:none;" onclick="move_ds_file();"><i class="fa-solid fa-arrow-right-arrow-left me-1"></i> Move</button>
</div>
</div>
<div class="tree well" id="ds-tree-container">
Expand Down
Loading
Loading