diff --git a/dev/publish-packages.md b/dev/publish-packages.md
index bd2ead664..48a62f224 100644
--- a/dev/publish-packages.md
+++ b/dev/publish-packages.md
@@ -56,14 +56,14 @@ Create a new release draft
- Title:
```
-Kappa Software Suite version 4.1.3
+Kappa Software Suite version v4.1.3
```
- Text:
```
v4.1.3
-Kappa Software Suite version 4.1.3
+Kappa Software Suite version v4.1.3
```
Set last release as previous tag, then click _Generate release notes_ and edit results to keep what's relevant
@@ -74,12 +74,17 @@ Add files for:
Kappapp_for_linux.tar.gz
Kappapp_for_mac_os_10.15.zip
Kappapp_for_windows.zip
+```
+taken from nightly builds: https://tools.kappalanguage.org/nightly-builds/
+
+Source code from release tag https://github.com/Kappa-Dev/KappaTools/releases/tag/v4.1.3 should appear automatically
+
+```
Source code (zip)
Source code (tar.gz)
```
-electron are from nightly builds: https://tools.kappalanguage.org/nightly-builds/
-Source code from release tag https://github.com/Kappa-Dev/KappaTools/releases/tag/v4.1.3
+
### Pip
diff --git a/gui/state/runtime_web_workers.ml b/gui/state/runtime_web_workers.ml
index 19d68a7d7..57f31294f 100644
--- a/gui/state/runtime_web_workers.ml
+++ b/gui/state/runtime_web_workers.ml
@@ -224,7 +224,9 @@ class runtime_kasim_as_web_worker () : Api.concrete_manager =
without_kasim#terminate;
kasim_worker##terminate
- method is_computing = without_kasim#is_computing || self#sim_is_computing
+ method is_computing =
+ without_kasim#is_computing || self#sim_is_computing
+ || self#story_is_computing
method project_parse ~patternSharing overwrites =
let simulation_load (patternSharing : Pattern.sharing_level)
@@ -236,6 +238,7 @@ class runtime_kasim_as_web_worker () : Api.concrete_manager =
overwrites
end
+(* TODO: deprecate this? *)
class runtime_kasim_embedded_in_main_thread () : Api.concrete_manager =
let system_process : Kappa_facade.system_process =
object
@@ -262,7 +265,8 @@ class runtime_kasim_embedded_in_main_thread () : Api.concrete_manager =
without_kasim#terminate;
() (*TODO*)
- method is_computing = true (*TODO*)
+ method is_computing =
+ without_kasim#is_computing || self#is_computing || self#story_is_computing
method project_parse ~patternSharing overwrites =
let simulation_load patternSharing parsing_compil overwrites =
diff --git a/gui/state/state_project.ml b/gui/state/state_project.ml
index ebc4bc1ef..b9b63e8db 100644
--- a/gui/state/state_project.ml
+++ b/gui/state/state_project.ml
@@ -210,13 +210,13 @@ let set_show_non_weakly_reversible_transitions
update_parameters (fun param ->
{ param with show_non_weakly_reversible_transitions })
-let update_state me project_catalog default_parameters project_parameters =
- me.project_manager#project_parse ~patternSharing:Pattern.Compatible_patterns
- []
+let update_state project project_catalog default_parameters project_parameters =
+ project.project_manager#project_parse
+ ~patternSharing:Pattern.Compatible_patterns []
>>= fun (out : unit Api.result) ->
set_state
{
- project_current = Some me;
+ project_current = Some project;
project_catalog;
default_parameters;
project_parameters;
@@ -224,8 +224,9 @@ let update_state me project_catalog default_parameters project_parameters =
};
Lwt.return out
-let computing_watcher manager setter =
+let computing_watcher (manager : Api.concrete_manager) (setter : bool -> unit) =
let delay = 1. in
+ (* Note: cancel logic seems not to be implemented? *)
let cancelled = ref false in
let rec loop () =
setter manager#is_computing;
@@ -254,7 +255,7 @@ let add_project is_new project_id : unit Api.lwt_result =
let project_watcher_cancel =
computing_watcher project_manager (set_computes ?step:None)
in
- let me =
+ let project : a_project =
{
project_id;
project_manager;
@@ -267,9 +268,9 @@ let add_project is_new project_id : unit Api.lwt_result =
Mods.StringMap.add project_id default_parameters
state_va.project_parameters
in
- Lwt.return (Result_util.ok (me, me :: catalog, params))))
- >>= Api_common.result_bind_with_lwt ~ok:(fun (me, catalog, params) ->
- update_state me catalog state_va.default_parameters params)
+ Lwt.return (Result_util.ok (project, project :: catalog, params))))
+ >>= Api_common.result_bind_with_lwt ~ok:(fun (project, catalog, params) ->
+ update_state project catalog state_va.default_parameters params)
let create_project project_id = add_project true project_id
let set_project project_id = add_project false project_id
diff --git a/gui/ui/panel_preferences.ml b/gui/ui/panel_preferences.ml
index b3892fbd1..c0e4d9772 100644
--- a/gui/ui/panel_preferences.ml
+++ b/gui/ui/panel_preferences.ml
@@ -175,7 +175,11 @@ module InputPlotPeriod : Ui_common.Div = struct
end
module DivErrorMessage : Ui_common.Div = struct
- let id = "configuration_error_div"
+ let id_error = "configuration_error_div"
+ let id_alert = "configuration_alert_div"
+ let id = id_error
+ (* TODO: clean this id matter *)
+
let message_nav_inc_id = "panel_preferences_message_nav_inc_id"
let message_nav_dec_id = "panel_preferences_message_nav_dec_id"
let message_file_label_id = "panel_preferences_message_file_label"
@@ -276,14 +280,14 @@ module DivErrorMessage : Ui_common.Div = struct
let error_message =
Html.span
- ~a:[ Html.a_id id; Html.a_class [ "error-span" ] ]
+ ~a:[ Html.a_id id_error; Html.a_class [ "error-span" ] ]
[ Tyxml_js.R.Html.txt error_message_text ]
let alert_messages =
Html.div
~a:
[
- Html.a_id id;
+ Html.a_id id_alert;
Tyxml_js.R.Html.a_class
(React.S.bind (Hooked.S.to_react_signal State_error.errors)
(fun error ->
diff --git a/gui/ui/panel_projects_controller.ml b/gui/ui/panel_projects_controller.ml
index 10ab1cf42..078f0a84b 100644
--- a/gui/ui/panel_projects_controller.ml
+++ b/gui/ui/panel_projects_controller.ml
@@ -8,11 +8,14 @@
open Lwt.Infix
-let refresh r =
- let r' = State_file.sync ~reset:true () in
- let r'' = State_simulation.refresh () in
- r' >>= fun r' ->
- r'' >>= fun r'' -> Lwt.return (Api_common.result_combine [ r; r'; r'' ])
+let refresh result_before =
+ let lwt_result_state_file_sync = State_file.sync ~reset:true () in
+ let lwt_result_simulation_refresh = State_simulation.refresh () in
+ lwt_result_state_file_sync >>= fun result_state_file_sync ->
+ lwt_result_simulation_refresh >>= fun result_simulation_refresh ->
+ Lwt.return
+ (Api_common.result_combine
+ [ result_before; result_state_file_sync; result_simulation_refresh ])
let create_project (project_id : string) : unit =
Common.async __LOC__ (fun () ->
diff --git a/gui/ui/panel_tabs/tab_stories.ml b/gui/ui/panel_tabs/tab_stories.ml
index 16b38959a..153cf0b64 100644
--- a/gui/ui/panel_tabs/tab_stories.ml
+++ b/gui/ui/panel_tabs/tab_stories.ml
@@ -191,7 +191,7 @@ let rec inspect_stories () =
>>= Result_util.fold
~ok:(fun is_computing ->
if is_computing && React.S.value tab_is_active then
- Js_of_ocaml_lwt.Lwt_js.sleep 3. >>= inspect_stories
+ Js_of_ocaml_lwt.Lwt_js.sleep 1. >>= inspect_stories
else
Lwt.return_unit)
~error:(fun _ -> Lwt.return_unit)
diff --git a/tests/playwright/procedure.spec.ts b/tests/playwright/procedure.spec.ts
index acf20d4ec..96ea6c6f4 100644
--- a/tests/playwright/procedure.spec.ts
+++ b/tests/playwright/procedure.spec.ts
@@ -1,6 +1,8 @@
// Kappapp tests
// Note: trace snapshots that should be taken by playwright are not (absent on right of ui `npx playwright test --ui`)
+// TODO: test with embedded Kasim? deprecate it?
+
import { test, expect, type Page } from '@playwright/test';
import * as utils from './webapp_utils';
@@ -39,8 +41,7 @@ test.describe('Editor tab', () => {
await editor.press('Backspace');
// (useless comment to match brackets { {)
await utils.expect_error(page, [
- " « 1/1 » [abc.ka] invalid internal state or missing '}' ",
- " invalid internal state or missing '}' ",
+ " « 1/1 » [abc.ka] invalid internal state or missing '}' "
]);
await editor_cancel();
await utils.expect_no_error(page);
@@ -48,8 +49,7 @@ test.describe('Editor tab', () => {
await editor.fill('\n%agent: D(a{u p})');
await utils.expect_error(page, [
- " « 1/1 » [abc.ka] Dead agent D ",
- " Dead agent D ",
+ " « 1/1 » [abc.ka] Dead agent D "
]);
await editor_to_line(25);
@@ -60,8 +60,7 @@ test.describe('Editor tab', () => {
await editor.fill("\n'd' D(a{p}) -> D(a{u}) @ 1");
// await page.locator('#panel_preferences_message_nav_inc_id').click();
await utils.expect_error(page, [
- " « 1/1 » [abc.ka] Dead rule 'd' ",
- " Dead rule 'd' ",
+ " « 1/1 » [abc.ka] Dead rule 'd' "
]);
await editor_cancel();
await editor_cancel();
@@ -95,7 +94,7 @@ test.describe('Editor tab', () => {
test('influences', async ({ page }) => {
const opts_screen = { threshold: 0.4 }
await utils.open_app_with_model(page, abc_ka);
- await page.getByRole('tab', { name: 'influences' }).click();
+ await page.locator('#navinfluences').click();
await expect.soft(page.locator('#influences-table')).toHaveScreenshot();
await expect.soft(page.getByRole('cell', { name: 'Navigate through the nodes' })).toBeVisible();
await page.getByRole('button', { name: 'First node' }).click();
@@ -130,7 +129,7 @@ test.describe('Editor tab', () => {
test('constraints_and_polymers_1', async ({ page }) => {
await utils.open_app_with_model(page, abc_ka);
- await page.getByRole('tab', { name: 'constraints' }).click();
+ await page.locator('#navconstraints').click();
await expect.soft(constraint_locator(page, 0)).toHaveText(
`A(c) => [ A(c[.]) v A(c[x1.C]) v A(c[x2.C]) ]
A(x) => [ A(x[.]) v A(x[x.B]) ]
@@ -145,7 +144,7 @@ C(x2) => [ C(x2{u}) v C(x2{p}) ]
"C() => [ C(x1{p}[.],x2{u}[.]) v C(x1{p}[.],x2{u}[c.A]) v C(x1{p}[.],x2{p}[.]) v C(x1{u}[c.A],x2{u}[.]) v C(x1{u}[.],x2{u}[.]) ]"
);
- await page.getByRole('tab', { name: 'polymers' }).click();
+ await page.locator('#navpolymers').click();
await expect.soft(page.getByRole('paragraph')).toHaveText(
"The size of biomolecular compounds is uniformly bounded."
);
@@ -153,7 +152,7 @@ C(x2) => [ C(x2{u}) v C(x2{p}) ]
test('constraints_and_polymers_2', async ({ page }) => {
await utils.open_app_with_model(page, poly_ka);
- await page.getByRole('tab', { name: 'polymers' }).click();
+ await page.locator('#navpolymers').click();
await expect.soft(page.getByRole('paragraph')).toHaveText(
`The following bonds may form arbitrary long chains of agents:
@@ -170,7 +169,7 @@ A(c[1]),C(a[1])
test('constraints_and_polymers_3', async ({ page }) => {
await utils.open_app_with_model(page, local_views_slide_69_ka, true);
- await page.getByRole('tab', { name: 'constraints' }).click();
+ await page.locator('#navconstraints').click();
await expect.soft(constraint_locator(page, 0)).toHaveText(
`E(x) => [ E(x[.]) v E(x[x.R]) ]
R(C) => [ R(C[.]) v R(C[CN.R]) ]
@@ -200,14 +199,13 @@ R(CN[C.R],CR[CR.R]) => R(CN[2],CR[1]),R(C[2],CR[1])
""
);
await utils.expect_error(page, [
- " « 1/1 » [model.ka] Dead rule R(CR[1] C[2]), R(CN[2]), R(CR[1]) -> R(CR[1] C[2]), R(CN[2]), R(CR[1]) ",
- " Dead rule R(CR[1] C[2]), R(CN[2]), R(CR[1]) -> R(CR[1] C[2]), R(CN[2]), R(CR[1]) "
+ " « 1/1 » [model.ka] Dead rule R(CR[1] C[2]), R(CN[2]), R(CR[1]) -> R(CR[1] C[2]), R(CN[2]), R(CR[1]) "
]);
});
test('constraints_and_polymers_4', async ({ page }) => {
await utils.open_app_with_model(page, counter_2_ka);
- await page.getByRole('tab', { name: 'constraints' }).click();
+ await page.locator('#navconstraints').click();
await expect.soft(constraint_locator(page, 4)).toHaveText(
`A() => A(c{[0 .. 2]})
`
@@ -233,7 +231,7 @@ test.describe('Simulation tools', () => {
await utils.set_pause_if(page, '[T] > 30');
await page.getByRole('button', { name: 'start' }).click();
await utils.wait_for_sim_stop(page, { timeout: 20000 });
- await page.getByRole('tab', { name: 'plot New' }).click();
+ await page.locator('#navplot').click();
await expect.soft(page.getByRole('img')).toHaveScreenshot();
await utils.set_pause_if(page, '[T] > 100');
await page.getByRole('button', { name: 'continue' }).click();
@@ -294,7 +292,7 @@ test.describe('Simulation tools', () => {
// Run simulation to 30, then 100, then test plot options
await utils.set_pause_if(page, '[T] > 30');
await page.getByRole('button', { name: 'start' }).click();
- await page.getByRole('tab', { name: 'DIN' }).click();
+ await page.locator('#navDIN').click();
await expectScreenShotDINTable();
await utils.testExports(page, '#export_din-export', 'flux', ['json', 'dot', 'html']);
@@ -319,27 +317,25 @@ test.describe('Simulation tools', () => {
await utils.set_pause_if(page, '[T] > 30');
await page.getByRole('button', { name: 'start' }).click();
await utils.apply_perturbation(page, '');
- await expect.soft(utils.get_error_field(page)).toHaveText("TODO");
+ await expect.soft(utils.get_error_field(page)).toHaveText(" « 1/1 » [] Problematic effect list ");
await utils.apply_perturbation(page, '$SNAPSHOT');
await utils.set_pause_if(page, '[T] > 60');
await page.getByRole('button', { name: 'continue' }).click();
await utils.apply_perturbation(page, '$SNAPSHOT "T60"');
// check log page
- await page.getByRole('tab', { name: 'log New' }).click();
+ await page.locator('#navlog').click();
await expect.soft(page.locator('#log div')).toHaveText(
- `Building initial simulation conditions... -variable declarations -rules -interventions -observables -update_domain construction 21 (sub)observables 37 navigation steps -initial conditionsUser-set seed used for simulation: 1%mod: [E] = 14599 do $SNAPSHOT ;%mod: [E] = 28929 do $SNAPSHOT \"T60\";
-`
+ `+ Building initial simulation conditions... -variable declarations -rules -interventions -observables -update_domain construction 21 (sub)observables 37 navigation steps -initial conditionsUser-set seed used for simulation: 1%mod: [E] = 14599 do $SNAPSHOT ;%mod: [E] = 28929 do $SNAPSHOT \"T60\";`
);
// go to tab snapshots and test display
- await page.getByRole('tab', { name: 'snapshot' }).click();
+ await page.locator('#navsnapshot').click();
const snapshot_display_loc = page.locator('.navcontent-view > .panel-scroll').first();
//const snapshot_map_display_loc = page.locator('#snapshot-map-display #map-container').first();
//const snapshot_map_display2_loc = page.locator('#snapshot-map-display div').first();
- await expect.soft(snapshot_display_loc).toHaveText(
- `// Snapshot [Event: 14599]
+ const snapshot0 = `// Snapshot [Event: 14599]
%def: "T0" "30.003207872859807"
%init: 60 /*3 agents*/ A(x[1] c[2]), B(x[1]), C(x1{p}[.] x2{u}[2])
@@ -354,11 +350,30 @@ test.describe('Simulation tools', () => {
%init: 474 /*1 agents*/ A(x[.] c[.])
-`);
+`;
+
+ const snapshot1 = `// Snapshot [Event: 28929]
+%def: "T0" "60.00175023164761"
+
+%init: 60 /*3 agents*/ B(x[1]), A(x[1] c[2]), C(x1{p}[.] x2{u}[2])
+%init: 75 /*3 agents*/ A(x[1] c[2]), B(x[1]), C(x1{u}[2] x2{u}[.])
+%init: 3 /*2 agents*/ A(x[.] c[1]), C(x1{u}[1] x2{u}[.])
+%init: 94 /*2 agents*/ A(x[.] c[1]), C(x1{p}[.] x2{u}[1])
+%init: 2925 /*1 agents*/ C(x1{u}[.] x2{u}[.])
+%init: 5022 /*1 agents*/ C(x1{p}[.] x2{p}[.])
+%init: 1821 /*1 agents*/ C(x1{p}[.] x2{u}[.])
+%init: 258 /*2 agents*/ B(x[1]), A(x[1] c[.])
+%init: 607 /*1 agents*/ B(x[.])
+%init: 510 /*1 agents*/ A(x[.] c[.])
+
+
+`;
+
+ await expect.soft(snapshot_display_loc).toHaveText(snapshot0);
await page.locator('#snapshot-select-id').selectOption('1');
- await expect.soft(snapshot_display_loc).toHaveText("TODO");
+ await expect.soft(snapshot_display_loc).toHaveText(snapshot1);
await page.locator('#snapshot-select-id').selectOption('0');
- await expect.soft(snapshot_display_loc).toHaveText("TODO");
+ await expect.soft(snapshot_display_loc).toHaveText(snapshot0);
await page.locator('#format_select_id').selectOption('Graph');
await expect.soft(snapshot_display_loc).toHaveScreenshot();
await page.locator('.navcontent-view > div:nth-child(3)').click();
@@ -392,26 +407,30 @@ test.describe('Simulation tools', () => {
// Generate two snapshots
await utils.set_pause_if(page, '[T] > 30');
await page.getByRole('button', { name: 'start' }).click();
- await utils.apply_perturbation(page, '$PRINT "time: ".[T] > "time.txt"');
- await utils.apply_perturbation(page, '$PRINT \'AB\' > "ab.txt"');
+ const print_time = '$PRINT "time: ".[T] > "time.txt"';
+ const print_ab = '$PRINT \'AB\' > "ab.txt"';
+ await utils.apply_perturbation(page, print_time);
+ await utils.apply_perturbation(page, print_ab);
await utils.set_pause_if(page, '[T] > 60');
await page.getByRole('button', { name: 'continue' }).click();
- await utils.apply_perturbation(page, '$PRINT "time: ".[T] > "time.txt"');
- await utils.apply_perturbation(page, '$PRINT \'AB\' > "ab.txt"');
+ await utils.apply_perturbation(page, print_time);
+ await utils.apply_perturbation(page, print_ab);
// check log page
- // await page.getByRole('tab', { name: 'log New' }).click();
- await page.locator('#navtabs').nth(1).click();
+ await page.locator('#navlog').click();
await expect.soft(page.locator('#log div')).toHaveText(
` + Building initial simulation conditions... -variable declarations -rules -interventions -observables -update_domain construction 21 (sub)observables 37 navigation steps -initial conditionsUser-set seed used for simulation: 1%mod: [E] = 14599 do $PRINTF ("time: ".[T]) > "time.txt";%mod: [E] = 14599 do $PRINTF (AB) > "ab.txt";%mod: [E] = 28929 do $PRINTF ("time: ".[T]) > "time.txt";%mod: [E] = 28929 do $PRINTF (AB) > "ab.txt";`
);
- await page.locator('#navtabs').nth(5).click();
+
+ // outputs page
+ await page.locator('#navoutputs').click();
+ const outputs_display = page.locator('.show > .navcontent-view > .panel-scroll');
await page.locator('#output-select-id').selectOption('time.txt');
- await expect.soft(page.getByRole('paragraph')).toHaveText("TODO");
+ await expect.soft(outputs_display).toHaveText("time: 30.0032078729time: 60.0017502316");
await page.locator('.list-group-item').click();
await page.locator('#output-select-id').selectOption('ab.txt');
- await expect.soft(page.getByRole('paragraph')).toHaveText("TODO");
+ await expect.soft(outputs_display).toHaveText("394393");
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'All outputs' }).click();
@@ -424,7 +443,7 @@ test.describe('Simulation tools', () => {
test.describe('stories', () => {
- test('stories', async ({ page }) => {
+ async function setup_stories(page: Page) {
await utils.open_app_with_model(page, causality_slide_10_ka, true);
await utils.setSeed(page, 1);
@@ -436,50 +455,211 @@ test.describe('stories', () => {
await page.getByRole('button', { name: 'start' }).click();
await utils.wait_for_sim_stop(page, { timeout: 20000 });
- async function expect_texts_story_logs(page: Page, text_info_log: string, text_computation_log: string) {
- await page.getByRole('tab', { name: 'story_info_log' }).click();
- await expect.soft(page.locator('#story_info_log')).toHaveText(text_info_log);
- await page.getByRole('tab', { name: 'stories_computation_log' }).click();
- await expect.soft(page.locator('#story_computation_log')).toHaveText(text_computation_log);
- await page.getByRole('tab', { name: 'story_info_log' }).click();
- }
+ // go to stories tab
+ await page.locator('#navstories').click();
+ // uncheck weakly
+ await page.getByRole('checkbox', { name: 'Weakly' }).uncheck();
+ }
- async function computeStoriesAndWait(page: Page) {
- await page.getByRole('button', { name: 'Launch' }).click();
- // wait for icon in project to be checkmark
- await utils.wait_for_project_ready_status(page, { timeout: 30000 });
- }
+ async function expect_texts_story_logs(page: Page, text_info_log: string, text_computation_log: string) {
+ await page.getByRole('tab', { name: 'story_info_log' }).click();
+ await expect.soft(page.locator('#story_info_log')).toHaveText(text_info_log);
+ await page.getByRole('tab', { name: 'stories_computation_log' }).click();
+ await expect.soft(page.locator('#stories_computation_log')).toHaveText(text_computation_log);
+ await page.getByRole('tab', { name: 'story_info_log' }).click();
+ }
+
+ async function computeStoriesAndWait(page: Page) {
+ await page.getByRole('button', { name: 'Launch' }).click();
+ // wait for icon in project to be busy `refresh`, the be back to checkmark
+ // (the `post` to the worker takes some time, which during when the icon is not refreshed)
+ // TODO: update this when icon refresh is fixed
+ await utils.wait_for_project_ready_status(page, { timeout: 30000 }, true);
+ }
- async function computeStoriesAndTest(page: Page, text_info_log: string, text_computation_log: string) {
- await computeStoriesAndWait(page);
- await expect_texts_story_logs(page, text_info_log, text_computation_log);
+ async function computeStoriesAndTest(page: Page, text_info_log: string, text_computation_log: string, expect_screenshot: boolean = true) {
+ await computeStoriesAndWait(page);
+ await expect_texts_story_logs(page, text_info_log, text_computation_log);
+ if (expect_screenshot) {
await expect.soft(page.getByRole('img')).toHaveScreenshot();
}
+ }
- // compare stories
- await page.getByRole('tab', { name: 'stories' }).click();
- await page.getByRole('checkbox', { name: 'Weakly' }).uncheck();
- await computeStoriesAndTest(page, "TODO", "TODO");
+ test('Empty', async ({ page }) => {
+ await setup_stories(page);
+ // No screenshot test as no stories causes no image to locate
+ await computeStoriesAndTest(page, "", `Starting Compression
+Compression completed
+0 stories
+`, false);
+ });
+
+ test('Weakly', async ({ page }) => {
+ await setup_stories(page);
+ await page.getByRole('checkbox', { name: 'Weakly' }).check();
+ await computeStoriesAndTest(page,
+ `ids: 11, 19, 24, 29, 33, 36, 37, 39, 49, 52, 55
+t=0.0975547254717, 0.153578551271, 0.171807609491, 0.195251616607,
+ 0.211330564253, 0.218683483843, 0.219103111568, 0.231032193424,
+ 0.275917880116, 0.287969294047, 0.299259752742
+event=2825, 3877, 4260, 4765, 5067, 5218, 5225, 5477, 6336, 6579, 6766`,
+ `Starting Compression
+Start one weak compression
+Start one weak compression
+Start one weak compression
+Compression completed
+3 stories
+`);
+ });
+ test('Strongly', async ({ page }) => {
+ await setup_stories(page);
await page.getByRole('checkbox', { name: 'Strongly' }).check();
- await page.getByRole('checkbox', { name: 'Strongly' }).uncheck();
- await computeStoriesAndTest(page, "TODO", "TODO");
- await page.getByRole('checkbox', { name: 'Causal' }).check();
- await page.getByRole('checkbox', { name: 'Causal' }).uncheck();
- await computeStoriesAndTest(page, "TODO", "TODO");
+ await computeStoriesAndTest(page,
+ `ids: 11, 19, 24, 29, 33, 36, 37, 39, 49, 52, 55, 5, 8, 21, 27, 28, 30, 31,
+ 32, 41, 42, 46, 47, 50, 51, 56, 1, 2, 3, 4, 6, 7, 9, 10, 12, 13, 14, 15,
+ 16, 17, 18, 20, 22, 23, 25, 26, 34, 35, 38, 40, 43, 44, 45, 48, 53, 54
+t=0.0975547254717, 0.153578551271, 0.171807609491, 0.195251616607,
+ 0.211330564253, 0.218683483843, 0.219103111568, 0.231032193424,
+ 0.275917880116, 0.287969294047, 0.299259752742, 0.0500738649842,
+ 0.0884797831425, 0.161217577071, 0.18332499113, 0.186583686508,
+ 0.195789013044, 0.198978506925, 0.205627414085, 0.250574592161,
+ 0.26335509208, 0.272985766182, 0.274937277942, 0.28313183861,
+ 0.28605881908, 0.299316030636, 0.0137256652424, 0.0424530222206,
+ 0.0449356024089, 0.0459328458692, 0.0657465273502, 0.0702375030214,
+ 0.0958494110054, 0.0975448431689, 0.101919958829, 0.1115605239,
+ 0.117581700446, 0.125390166878, 0.126609656237, 0.139072907093,
+ 0.150316974123, 0.154324059758, 0.163465828786, 0.167329986128,
+ 0.172870749643, 0.181525571054, 0.21396617172, 0.217633967976,
+ 0.228163976202, 0.236188125158, 0.265437118532, 0.267501895402,
+ 0.272427852987, 0.274945449678, 0.28820790984, 0.288299009288
+event=2825, 3877, 4260, 4765, 5067, 5218, 5225, 5477, 6336, 6579, 6766, 1815,
+ 2638, 4029, 4505, 4581, 4775, 4822, 4950, 5834, 6102, 6272, 6316, 6491,
+ 6544, 6767, 1103, 1662, 1709, 1728, 2159, 2246, 2781, 2823, 2900, 3076,
+ 3184, 3329, 3347, 3577, 3793, 3890, 4083, 4170, 4282, 4459, 5118, 5200,
+ 5423, 5563, 6127, 6167, 6263, 6317, 6581, 6583`,
+ `Starting Compression
+Start one strong compression
+Start one strong compression
+Start one strong compression
+Compression completed
+1 stories
+`);
+
+ });
+ test('Causal + select stories', async ({ page }) => {
+ await setup_stories(page);
+ await page.getByRole('checkbox', { name: 'Causal' }).check();
+ const computation_log = `Starting Compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Start one causal compression
+Compression completed
+26 stories
+`;
+
+ await computeStoriesAndTest(page,
+ `ids: 56 t=0.299316030636 event=6767`,
+ computation_log);
await page.getByRole('combobox').selectOption('0');
await expect.soft(page.getByRole('img')).toHaveScreenshot();
- await expect_texts_story_logs(page, "TODO", "TODO");
+ await expect_texts_story_logs(page,
+ `ids: 43, 26, 22, 20, 16, 15, 10, 9, 6, 4, 3, 2, 1
+t=0.265437118532, 0.181525571054, 0.163465828786, 0.154324059758,
+ 0.126609656237, 0.125390166878, 0.0975448431689, 0.0958494110054,
+ 0.0657465273502, 0.0459328458692, 0.0449356024089, 0.0424530222206,
+ 0.0137256652424
+event=6127, 4459, 4083, 3890, 3347, 3329, 2823, 2781, 2159, 1728, 1709, 1662,
+ 1103`,
+ computation_log);
await page.getByRole('combobox').selectOption('2');
await expect.soft(page.getByRole('img')).toHaveScreenshot();
- await expect_texts_story_logs(page, "TODO", "TODO");
+ await expect_texts_story_logs(page,
+ `ids: 35, 14, 7 t=0.217633967976, 0.117581700446, 0.0702375030214
+event=5200, 3184, 2246`,
+ computation_log);
+ });
+ test('Weakly + Strongly', async ({ page }) => {
+ await setup_stories(page);
await page.getByRole('checkbox', { name: 'Weakly' }).check();
await page.getByRole('checkbox', { name: 'Strongly' }).check();
- await computeStoriesAndTest(page, "TODO", "TODO");
+ await computeStoriesAndTest(page,
+ `ids: 11, 19, 24, 29, 33, 36, 37, 39, 49, 52, 55
+t=0.0975547254717, 0.153578551271, 0.171807609491, 0.195251616607,
+ 0.211330564253, 0.218683483843, 0.219103111568, 0.231032193424,
+ 0.275917880116, 0.287969294047, 0.299259752742
+event=2825, 3877, 4260, 4765, 5067, 5218, 5225, 5477, 6336, 6579, 6766`
+ ,
+ `Starting Compression
+Start one weak compression
+Start one weak compression
+Start one weak compression
+Start one strong compression
+Start one strong compression
+Start one strong compression
+Compression completed
+3 stories
+`);
+ });
+ test('Trace download', async ({ page }) => {
+ await setup_stories(page);
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'get trace' }).click();
const download = await downloadPromise;
@@ -494,9 +674,10 @@ test.describe('projects_and_files', () => {
await utils.setSeed(page, 1);
// open new project
+ const project_name = 'new_project'
await page.getByRole('list').locator('a').nth(1).click();
await page.getByPlaceholder('project new').click();
- await page.getByPlaceholder('project new').fill('test');
+ await page.getByPlaceholder('project new').fill(project_name);
await page.getByPlaceholder('project new').press('Enter');
// add new file "abc.ka" and fill it
await page.getByRole('button', { name: 'File' }).click();
@@ -511,8 +692,10 @@ test.describe('projects_and_files', () => {
const contact_map = page.locator('#map-container');
await expect.soft(contact_map).toHaveScreenshot();
await page.getByRole('list').locator('a').nth(1).click();
+ await page.waitForTimeout(2000);
await expect.soft(contact_map).toHaveScreenshot();
await page.getByRole('list').locator('a').first().click();
+ await page.waitForTimeout(2000);
await expect.soft(contact_map).toHaveScreenshot();
// TODO: Could also check simulation results
@@ -534,8 +717,10 @@ test.describe('projects_and_files', () => {
await page.getByRole('button', { name: 'File' }).click();
await page.locator('#menu-editor-file-close-li').click();
await page.getByRole('button', { name: 'File' }).click();
+ const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('#menu-editor-file-open-li').click();
- await page.locator('body').setInputFiles(downloaded_path);
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles(downloaded_path);
// write other file and check contact map
await page.getByRole('button', { name: 'File' }).click();
@@ -564,7 +749,7 @@ test.describe('projects_and_files', () => {
await utils.set_pause_if(page, '[T] > 30');
await page.getByRole('button', { name: 'start' }).click();
await utils.wait_for_sim_stop(page, { timeout: 20000 });
- await page.getByRole('tab', { name: 'plot New' }).click();
+ await page.locator('#navplot').click();
await expect.soft(page.getByRole('img')).toHaveScreenshot();
});
diff --git a/tests/playwright/webapp_utils.ts b/tests/playwright/webapp_utils.ts
index e051ed7db..2565c2e00 100644
--- a/tests/playwright/webapp_utils.ts
+++ b/tests/playwright/webapp_utils.ts
@@ -22,22 +22,42 @@ function timeout_of_options(options?: { timeout?: number | undefined; visible?:
return timeout;
}
+// TODO: test for contains and not ==
+
// Used as playwright does not seemed to offer a way to have this logic
-async function expect_locator_toHaveInnerHtml(page: Page, locator: Locator, value: any, timeout: number) {
- const end_time = Date.now() + timeout;
- var is_equal = false;
+async function expect_locator_toHaveInnerHtml(page: Page, locator: Locator, value: any, timeout: number, retry_timeout: number = 500, allow_include: boolean = false) {
+ const end_time: number = Date.now() + timeout;
+ var is_equal: boolean = false;
while (Date.now() < end_time && !(is_equal)) {
const html = (await locator.innerHTML());
- is_equal = (html == value);
- await page.waitForTimeout(1000);
+ if (allow_include) {
+ is_equal = (html.includes(value));
+ } else {
+ is_equal = (html == value);
+ };
+ await page.waitForTimeout(retry_timeout);
}
expect(is_equal).toBeTruthy();
}
-export async function wait_for_project_ready_status(page: Page, options?: { timeout?: number | undefined; visible?: boolean | undefined; } | undefined) {
- // wait for icon in project to be checkmark
+export async function wait_for_project_ready_status(page: Page, options?: { timeout?: number | undefined; visible?: boolean | undefined; } | undefined, check_busy: boolean = false, project_name: string | undefined) {
const timeout = timeout_of_options(options);
- await expect_locator_toHaveInnerHtml(page, page.getByRole('list').locator('a').first(), " default", timeout);
+ const locator_first_tab = page.getByRole('list').locator('a').first();
+
+ if (check_busy) {
+ // wait for the icon to go to busy state before waiting to be back to ready state
+ // [retry_timeout] is low to try not to miss if the change to "refresh" is fast
+ // (useful for stories computation that at the moment don't change the icon state rightaway)
+ await expect_locator_toHaveInnerHtml(page, locator_first_tab, "\"glyphicon glyphicon-refresh\"", timeout, 50, true);
+ }
+
+ // wait for project change if provided
+ if (project_name !== undefined) {
+ await expect_locator_toHaveInnerHtml(page, locator_first_tab, project_name, timeout, 500, true);
+ }
+
+ // wait for icon in project to be checkmark
+ await expect_locator_toHaveInnerHtml(page, locator_first_tab, "\"glyphicon glyphicon-ok\"", timeout, 500, true);
}
export async function wait_for_sim_stop(page: Page, options?: { timeout?: number | undefined; visible?: boolean | undefined; } | undefined) {
@@ -85,7 +105,8 @@ export async function open_app_with_model(page: Page, url_protocol_relative: str
}
export function get_error_field(page: Page) {
- return page.locator('#configuration_error_div');
+ // return page.locator('#configuration_error_div');
+ return page.locator('#configuration_alert_div');
}
export async function expect_error(page: Page, text: string[]): Promise {
@@ -94,8 +115,7 @@ export async function expect_error(page: Page, text: string[]): Promise {
export async function expect_no_error(page: Page): Promise {
await expect_error(page, [
- " « » ",
- "",
+ " « » "
]);
}