= (index: number, contact: Contact): any => {
+ return contact.id;
+ };
+}
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/assets/.gitkeep b/projects/e2e/workbench-application-platform/contact-app/src/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/assets/contact.data.json b/projects/e2e/workbench-application-platform/contact-app/src/assets/contact.data.json
new file mode 100644
index 000000000..586c4df12
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/assets/contact.data.json
@@ -0,0 +1,2740 @@
+[
+ {
+ "id": "1",
+ "firstname": "Marley",
+ "lastname": "Chidzoy",
+ "email": "mchidzoy0@oracle.com",
+ "phone": "424-561-5508",
+ "street": "08 Monterey Court",
+ "city": "New York",
+ "relatedContactIds": [
+ "17",
+ "1",
+ "13",
+ "5",
+ "91",
+ "57",
+ "44",
+ "32",
+ "4",
+ "16",
+ "86",
+ "43",
+ "40",
+ "67",
+ "13",
+ "65",
+ "22",
+ "86",
+ "11",
+ "27",
+ "94",
+ "82",
+ "91",
+ "84",
+ "80",
+ "61",
+ "80",
+ "11",
+ "5"
+ ]
+ },
+ {
+ "id": "2",
+ "firstname": "Arlen",
+ "lastname": "Bossel",
+ "email": "abossel1@bandcamp.com",
+ "phone": "205-853-0895",
+ "street": "1 Moulton Terrace",
+ "city": "Los Angeles",
+ "relatedContactIds": [
+ "48",
+ "26",
+ "43",
+ "92",
+ "70",
+ "25",
+ "78",
+ "30"
+ ]
+ },
+ {
+ "id": "3",
+ "firstname": "Sigismond",
+ "lastname": "Mulqueeny",
+ "email": "smulqueeny2@oaic.gov.au",
+ "phone": "737-861-1860",
+ "street": "15 Hintze Point",
+ "city": "Chicago",
+ "relatedContactIds": [
+ "33",
+ "97",
+ "82",
+ "88",
+ "76",
+ "99",
+ "16",
+ "39",
+ "18",
+ "75",
+ "38",
+ "11",
+ "52",
+ "79"
+ ]
+ },
+ {
+ "id": "4",
+ "firstname": "Brooke",
+ "lastname": "Hulmes",
+ "email": "bhulmes3@1und1.de",
+ "phone": "667-900-2300",
+ "street": "5 Moland Lane",
+ "city": "Houston",
+ "relatedContactIds": [
+ "82",
+ "7",
+ "95",
+ "63",
+ "16",
+ "47",
+ "14",
+ "37",
+ "14",
+ "72",
+ "53",
+ "11"
+ ]
+ },
+ {
+ "id": "5",
+ "firstname": "Robbie",
+ "lastname": "Bannon",
+ "email": "rbannon4@ebay.co.uk",
+ "phone": "127-927-7189",
+ "street": "685 Hansons Pass",
+ "city": "Philadelphia",
+ "relatedContactIds": [
+ "30",
+ "96",
+ "93",
+ "6",
+ "100",
+ "85",
+ "100",
+ "95",
+ "11",
+ "75",
+ "88",
+ "35",
+ "26",
+ "49",
+ "96",
+ "36",
+ "21",
+ "87",
+ "18",
+ "53"
+ ]
+ },
+ {
+ "id": "6",
+ "firstname": "Chastity",
+ "lastname": "Schumacher",
+ "email": "cschumacher5@youtube.com",
+ "phone": "627-896-4939",
+ "street": "2 Badeau Center",
+ "city": "Phoenix",
+ "relatedContactIds": [
+ "45",
+ "99",
+ "90",
+ "35",
+ "58",
+ "49",
+ "35",
+ "45",
+ "14",
+ "88",
+ "33",
+ "89",
+ "54",
+ "11",
+ "35",
+ "98",
+ "40",
+ "22",
+ "51",
+ "52",
+ "33"
+ ]
+ },
+ {
+ "id": "7",
+ "firstname": "Ric",
+ "lastname": "Spelsbury",
+ "email": "rspelsbury6@over-blog.com",
+ "phone": "120-113-6252",
+ "street": "52558 Del Sol Road",
+ "city": "San Antonio",
+ "relatedContactIds": [
+ "27",
+ "67",
+ "13",
+ "31",
+ "60",
+ "10",
+ "82",
+ "56",
+ "72",
+ "1",
+ "27",
+ "53",
+ "88",
+ "8",
+ "52",
+ "36",
+ "72",
+ "15",
+ "56",
+ "58",
+ "26",
+ "13",
+ "65",
+ "40",
+ "35",
+ "75",
+ "2",
+ "18"
+ ]
+ },
+ {
+ "id": "8",
+ "firstname": "Sherilyn",
+ "lastname": "Baselio",
+ "email": "sbaselio7@dion.ne.jp",
+ "phone": "336-625-1423",
+ "street": "19 Carey Point",
+ "city": "San Diego",
+ "relatedContactIds": [
+ "79",
+ "60",
+ "89",
+ "43",
+ "4",
+ "37",
+ "82",
+ "39",
+ "54",
+ "58",
+ "45",
+ "9",
+ "20",
+ "51",
+ "42",
+ "3",
+ "57",
+ "93",
+ "97",
+ "4",
+ "81"
+ ]
+ },
+ {
+ "id": "9",
+ "firstname": "Allianora",
+ "lastname": "Boldt",
+ "email": "aboldt8@cpanel.net",
+ "phone": "308-983-4275",
+ "street": "41 Blaine Road",
+ "city": "Dallas",
+ "relatedContactIds": [
+ "4",
+ "66",
+ "84",
+ "1",
+ "83",
+ "84",
+ "70",
+ "45",
+ "21",
+ "33",
+ "69",
+ "49",
+ "72",
+ "19",
+ "30",
+ "47",
+ "6",
+ "86",
+ "60",
+ "67",
+ "7",
+ "82",
+ "6",
+ "81",
+ "99",
+ "11"
+ ]
+ },
+ {
+ "id": "10",
+ "firstname": "Priscilla",
+ "lastname": "Duffitt",
+ "email": "pduffitt9@economist.com",
+ "phone": "919-470-6524",
+ "street": "967 Blaine Center",
+ "city": "10",
+ "relatedContactIds": [
+ "8",
+ "50",
+ "57",
+ "11",
+ "25",
+ "43",
+ "39",
+ "13",
+ "5",
+ "20",
+ "19",
+ "57"
+ ]
+ },
+ {
+ "id": "11",
+ "firstname": "Darcy",
+ "lastname": "Hammelberg",
+ "email": "dhammelberga@thetimes.co.uk",
+ "phone": "944-291-7076",
+ "street": "21621 Merry Trail",
+ "city": "New York",
+ "relatedContactIds": [
+ "1",
+ "53",
+ "53",
+ "3",
+ "88",
+ "56",
+ "49",
+ "47",
+ "38",
+ "39",
+ "64",
+ "51",
+ "15",
+ "27",
+ "35",
+ "97"
+ ]
+ },
+ {
+ "id": "12",
+ "firstname": "Kennie",
+ "lastname": "Klessmann",
+ "email": "kklessmannb@pagesperso-orange.fr",
+ "phone": "199-728-6721",
+ "street": "17 Garrison Circle",
+ "city": "New York",
+ "relatedContactIds": [
+ "35"
+ ]
+ },
+ {
+ "id": "13",
+ "firstname": "Zorana",
+ "lastname": "Jankovic",
+ "email": "zjankovicc@paypal.com",
+ "phone": "760-147-5671",
+ "street": "311 Del Mar Park",
+ "city": "New York",
+ "relatedContactIds": [
+ "57",
+ "73",
+ "79",
+ "5",
+ "45",
+ "22",
+ "99",
+ "39",
+ "19",
+ "23",
+ "68",
+ "47",
+ "8",
+ "51",
+ "12",
+ "49",
+ "19"
+ ]
+ },
+ {
+ "id": "14",
+ "firstname": "Cheryl",
+ "lastname": "Mattea",
+ "email": "cmattead@webs.com",
+ "phone": "356-935-1878",
+ "street": "7774 Crescent Oaks Road",
+ "city": "New York",
+ "relatedContactIds": [
+ "3",
+ "12",
+ "24",
+ "96",
+ "64",
+ "2",
+ "94",
+ "35",
+ "93",
+ "93",
+ "46",
+ "74",
+ "82",
+ "48",
+ "18",
+ "6",
+ "28",
+ "17",
+ "16"
+ ]
+ },
+ {
+ "id": "15",
+ "firstname": "Clarke",
+ "lastname": "Noden",
+ "email": "cnodene@google.ru",
+ "phone": "839-291-9187",
+ "street": "37 Anhalt Street",
+ "city": "New York",
+ "relatedContactIds": [
+ "6",
+ "97",
+ "66",
+ "61",
+ "36",
+ "74",
+ "38",
+ "76",
+ "86",
+ "25",
+ "37",
+ "42",
+ "90",
+ "91",
+ "27"
+ ]
+ },
+ {
+ "id": "16",
+ "firstname": "Giralda",
+ "lastname": "Fackrell",
+ "email": "gfackrellf@ucsd.edu",
+ "phone": "996-969-6842",
+ "street": "56509 Tennyson Park",
+ "city": "New York",
+ "relatedContactIds": [
+ "41",
+ "86",
+ "75",
+ "49",
+ "92",
+ "93",
+ "47",
+ "96",
+ "26",
+ "57",
+ "76",
+ "75",
+ "55",
+ "5",
+ "88",
+ "21",
+ "25",
+ "4",
+ "87",
+ "11",
+ "63",
+ "16",
+ "51",
+ "70",
+ "65"
+ ]
+ },
+ {
+ "id": "17",
+ "firstname": "Sheppard",
+ "lastname": "Bolstridge",
+ "email": "sbolstridgeg@wsj.com",
+ "phone": "982-513-7145",
+ "street": "00358 Monterey Terrace",
+ "city": "New York",
+ "relatedContactIds": [
+ "15",
+ "70",
+ "35",
+ "17",
+ "3",
+ "61",
+ "88",
+ "86",
+ "93",
+ "46",
+ "3"
+ ]
+ },
+ {
+ "id": "18",
+ "firstname": "Garvey",
+ "lastname": "McKinnon",
+ "email": "gmckinnonh@hatena.ne.jp",
+ "phone": "281-541-9252",
+ "street": "50809 High Crossing Alley",
+ "city": "Fort Worth",
+ "relatedContactIds": [
+ "21",
+ "49",
+ "20",
+ "44",
+ "47",
+ "16",
+ "73",
+ "5",
+ "84",
+ "57",
+ "61",
+ "63",
+ "67",
+ "99",
+ "45",
+ "9",
+ "24",
+ "39",
+ "18",
+ "66"
+ ]
+ },
+ {
+ "id": "19",
+ "firstname": "Elden",
+ "lastname": "Ring",
+ "email": "eringi@g.co",
+ "phone": "265-527-5671",
+ "street": "62470 Gina Plaza",
+ "city": "Fort Worth",
+ "relatedContactIds": [
+ "7",
+ "3",
+ "62",
+ "24",
+ "60",
+ "10",
+ "6",
+ "21"
+ ]
+ },
+ {
+ "id": "20",
+ "firstname": "Connie",
+ "lastname": "Ceschi",
+ "email": "cceschij@cafepress.com",
+ "phone": "687-266-4082",
+ "street": "70972 Stuart Avenue",
+ "city": "Fort Worth",
+ "relatedContactIds": [
+ "73",
+ "23",
+ "74",
+ "55",
+ "65",
+ "95",
+ "45",
+ "4",
+ "21",
+ "99",
+ "56",
+ "92",
+ "83",
+ "26",
+ "66",
+ "86",
+ "26",
+ "67",
+ "90",
+ "68",
+ "70",
+ "38",
+ "100",
+ "79",
+ "64",
+ "37",
+ "46",
+ "94"
+ ]
+ },
+ {
+ "id": "21",
+ "firstname": "Charmion",
+ "lastname": "Phillipp",
+ "email": "cphillippk@elpais.com",
+ "phone": "749-465-6113",
+ "street": "489 Lindbergh Parkway",
+ "city": "Fort Worth",
+ "relatedContactIds": [
+ "71",
+ "92",
+ "74",
+ "72",
+ "37",
+ "24",
+ "55",
+ "42",
+ "29",
+ "92",
+ "64",
+ "29",
+ "32",
+ "100",
+ "52",
+ "33",
+ "52",
+ "33",
+ "68",
+ "93",
+ "77",
+ "77",
+ "23",
+ "69",
+ "75",
+ "59",
+ "42"
+ ]
+ },
+ {
+ "id": "22",
+ "firstname": "Dane",
+ "lastname": "Palay",
+ "email": "dpalayl@howstuffworks.com",
+ "phone": "695-766-3982",
+ "street": "5444 Clyde Gallagher Terrace",
+ "city": "Fort Worth",
+ "relatedContactIds": [
+ "2",
+ "62",
+ "10",
+ "76",
+ "11",
+ "89",
+ "74",
+ "32",
+ "4",
+ "27",
+ "27",
+ "50",
+ "79",
+ "89",
+ "93",
+ "57",
+ "91",
+ "89",
+ "13",
+ "41",
+ "49",
+ "13",
+ "67",
+ "96",
+ "77"
+ ]
+ },
+ {
+ "id": "23",
+ "firstname": "Devy",
+ "lastname": "Bulleyn",
+ "email": "dbulleynm@ehow.com",
+ "phone": "996-426-0038",
+ "street": "7330 Novick Parkway",
+ "city": "Fort Worth",
+ "relatedContactIds": [
+ "33",
+ "60",
+ "93",
+ "47",
+ "3",
+ "39",
+ "20",
+ "11",
+ "34",
+ "19",
+ "90",
+ "52",
+ "87",
+ "86",
+ "74",
+ "84",
+ "29",
+ "56",
+ "73",
+ "65",
+ "23",
+ "78",
+ "33",
+ "88",
+ "92",
+ "30",
+ "76",
+ "92",
+ "89",
+ "57"
+ ]
+ },
+ {
+ "id": "24",
+ "firstname": "Marlo",
+ "lastname": "Angus",
+ "email": "mangusn@ted.com",
+ "phone": "693-385-3039",
+ "street": "2458 Mandrake Park",
+ "city": "Detroit",
+ "relatedContactIds": [
+ "99",
+ "7",
+ "68",
+ "78",
+ "41",
+ "91",
+ "7",
+ "36",
+ "31",
+ "25",
+ "65",
+ "77",
+ "19",
+ "1",
+ "62",
+ "96",
+ "95",
+ "47",
+ "87",
+ "41",
+ "17",
+ "42",
+ "38"
+ ]
+ },
+ {
+ "id": "25",
+ "firstname": "Cassandra",
+ "lastname": "Ingerson",
+ "email": "cingersono@hc360.com",
+ "phone": "172-961-9547",
+ "street": "1832 Northport Place",
+ "city": "Detroit",
+ "relatedContactIds": [
+ "23",
+ "7",
+ "100",
+ "77",
+ "21",
+ "7",
+ "5",
+ "39",
+ "64",
+ "32",
+ "23",
+ "84",
+ "54",
+ "66",
+ "59",
+ "26",
+ "7",
+ "77",
+ "22",
+ "14",
+ "88",
+ "83",
+ "4",
+ "18",
+ "52",
+ "68"
+ ]
+ },
+ {
+ "id": "26",
+ "firstname": "Felix",
+ "lastname": "Lilley",
+ "email": "flilleyp@cocolog-nifty.com",
+ "phone": "163-958-5543",
+ "street": "93585 Grasskamp Junction",
+ "city": "Detroit",
+ "relatedContactIds": [
+ "44",
+ "71",
+ "53",
+ "67",
+ "100",
+ "100"
+ ]
+ },
+ {
+ "id": "27",
+ "firstname": "Brigham",
+ "lastname": "Fidell",
+ "email": "bfidellq@quantcast.com",
+ "phone": "916-782-2894",
+ "street": "55067 Emmet Terrace",
+ "city": "Detroit",
+ "relatedContactIds": [
+ "22",
+ "31"
+ ]
+ },
+ {
+ "id": "28",
+ "firstname": "Darrell",
+ "lastname": "Yo",
+ "email": "dyor@psu.edu",
+ "phone": "829-854-6749",
+ "street": "3370 Moland Alley",
+ "city": "El Paso",
+ "relatedContactIds": [
+ "45",
+ "71",
+ "4",
+ "83",
+ "25",
+ "52",
+ "62",
+ "29",
+ "74",
+ "30",
+ "73",
+ "1",
+ "87",
+ "18",
+ "38",
+ "47",
+ "14",
+ "43",
+ "56",
+ "64",
+ "25",
+ "3",
+ "54",
+ "72",
+ "33",
+ "5",
+ "9"
+ ]
+ },
+ {
+ "id": "29",
+ "firstname": "Forster",
+ "lastname": "O'Gaven",
+ "email": "fogavens@auda.org.au",
+ "phone": "410-837-5493",
+ "street": "0 Sunnyside Junction",
+ "city": "El Paso",
+ "relatedContactIds": [
+ "21",
+ "16",
+ "10",
+ "79",
+ "71",
+ "34",
+ "89",
+ "10",
+ "80",
+ "44",
+ "96",
+ "93",
+ "93",
+ "2",
+ "91",
+ "14",
+ "85",
+ "39",
+ "25",
+ "91"
+ ]
+ },
+ {
+ "id": "30",
+ "firstname": "Fayina",
+ "lastname": "Durtnall",
+ "email": "fdurtnallt@creativecommons.org",
+ "phone": "613-332-4522",
+ "street": "49 Cascade Circle",
+ "city": "El Paso",
+ "relatedContactIds": [
+ "66",
+ "14",
+ "87",
+ "20",
+ "1",
+ "41",
+ "91",
+ "23",
+ "30",
+ "19",
+ "29",
+ "56",
+ "43",
+ "63",
+ "55",
+ "83"
+ ]
+ },
+ {
+ "id": "31",
+ "firstname": "Woodie",
+ "lastname": "Thompson",
+ "email": "wthompsonu@wp.com",
+ "phone": "218-925-3687",
+ "street": "3 Monument Parkway",
+ "city": "El Paso",
+ "relatedContactIds": [
+ "89",
+ "79",
+ "22",
+ "59",
+ "6",
+ "49"
+ ]
+ },
+ {
+ "id": "32",
+ "firstname": "Ozzy",
+ "lastname": "Emanuele",
+ "email": "oemanuelev@google.co.jp",
+ "phone": "580-405-2775",
+ "street": "0 American Ash Way",
+ "city": "El Paso",
+ "relatedContactIds": [
+ "21",
+ "42",
+ "100",
+ "2",
+ "21",
+ "35",
+ "10",
+ "63",
+ "60",
+ "12",
+ "74",
+ "12",
+ "36",
+ "81",
+ "96",
+ "31",
+ "74",
+ "30"
+ ]
+ },
+ {
+ "id": "33",
+ "firstname": "Reggis",
+ "lastname": "Silverston",
+ "email": "rsilverstonw@blogspot.com",
+ "phone": "181-855-1848",
+ "street": "16369 Rieder Road",
+ "city": "Memphis",
+ "relatedContactIds": [
+ "31",
+ "29"
+ ]
+ },
+ {
+ "id": "34",
+ "firstname": "Shane",
+ "lastname": "Hounsom",
+ "email": "shounsomx@sbwire.com",
+ "phone": "639-282-0481",
+ "street": "2564 Melrose Way",
+ "city": "Memphis",
+ "relatedContactIds": [
+ "79",
+ "85",
+ "50",
+ "75",
+ "87",
+ "11",
+ "89",
+ "37",
+ "5",
+ "78",
+ "11",
+ "41",
+ "54",
+ "92",
+ "46",
+ "95",
+ "93",
+ "63",
+ "45",
+ "35",
+ "66",
+ "16",
+ "62",
+ "100",
+ "86",
+ "98"
+ ]
+ },
+ {
+ "id": "35",
+ "firstname": "Audi",
+ "lastname": "Marriott",
+ "email": "amarriotty@vistaprint.com",
+ "phone": "394-428-1689",
+ "street": "189 Alpine Point",
+ "city": "Memphis",
+ "relatedContactIds": [
+ "1",
+ "4",
+ "99",
+ "19",
+ "45",
+ "18",
+ "14",
+ "24",
+ "73",
+ "68",
+ "41",
+ "44",
+ "3",
+ "83",
+ "16",
+ "94",
+ "24",
+ "36",
+ "51",
+ "84",
+ "45",
+ "54",
+ "33",
+ "43",
+ "47",
+ "73",
+ "10",
+ "99"
+ ]
+ },
+ {
+ "id": "36",
+ "firstname": "Starlin",
+ "lastname": "Stidston",
+ "email": "sstidstonz@scientificamerican.com",
+ "phone": "906-227-3210",
+ "street": "827 Blaine Point",
+ "city": "Memphis",
+ "relatedContactIds": [
+ "23",
+ "45",
+ "79",
+ "93",
+ "23"
+ ]
+ },
+ {
+ "id": "37",
+ "firstname": "Nathan",
+ "lastname": "Scrogges",
+ "email": "nscrogges10@gnu.org",
+ "phone": "154-685-9931",
+ "street": "886 Service Circle",
+ "city": "Memphis",
+ "relatedContactIds": [
+ "62",
+ "38",
+ "94",
+ "97"
+ ]
+ },
+ {
+ "id": "38",
+ "firstname": "Adeline",
+ "lastname": "McEnhill",
+ "email": "amcenhill11@baidu.com",
+ "phone": "727-842-2730",
+ "street": "0 Annamark Place",
+ "city": "Seattle",
+ "relatedContactIds": [
+ "63",
+ "95",
+ "11",
+ "46",
+ "61",
+ "44",
+ "59",
+ "1",
+ "92",
+ "86",
+ "11",
+ "47",
+ "78",
+ "9",
+ "64",
+ "24",
+ "76",
+ "54",
+ "44",
+ "71",
+ "73",
+ "42",
+ "1",
+ "4",
+ "10",
+ "54",
+ "90",
+ "37",
+ "64"
+ ]
+ },
+ {
+ "id": "39",
+ "firstname": "Alaster",
+ "lastname": "Sancto",
+ "email": "asancto12@prnewswire.com",
+ "phone": "409-222-0492",
+ "street": "8 Doe Crossing Center",
+ "city": "Seattle",
+ "relatedContactIds": [
+ "98",
+ "11",
+ "78",
+ "33",
+ "57",
+ "85",
+ "81",
+ "38",
+ "10",
+ "41",
+ "77",
+ "17",
+ "74",
+ "52",
+ "86",
+ "27",
+ "86",
+ "62",
+ "100"
+ ]
+ },
+ {
+ "id": "40",
+ "firstname": "Garret",
+ "lastname": "Turnock",
+ "email": "gturnock13@cbslocal.com",
+ "phone": "287-728-4389",
+ "street": "947 Utah Junction",
+ "city": "Seattle",
+ "relatedContactIds": [
+ "13",
+ "98",
+ "30",
+ "16",
+ "99",
+ "43",
+ "53",
+ "40"
+ ]
+ },
+ {
+ "id": "41",
+ "firstname": "Nappy",
+ "lastname": "Lenaghen",
+ "email": "nlenaghen14@delicious.com",
+ "phone": "786-344-7171",
+ "street": "54 Marquette Avenue",
+ "city": "Seattle",
+ "relatedContactIds": [
+ "34",
+ "69",
+ "25",
+ "42",
+ "19",
+ "48",
+ "48",
+ "28",
+ "52",
+ "32",
+ "69",
+ "78",
+ "74",
+ "86",
+ "43",
+ "35",
+ "62",
+ "32",
+ "43",
+ "16"
+ ]
+ },
+ {
+ "id": "42",
+ "firstname": "Eberhard",
+ "lastname": "Dawltrey",
+ "email": "edawltrey15@weather.com",
+ "phone": "374-975-6888",
+ "street": "1 Anhalt Trail",
+ "city": "Seattle",
+ "relatedContactIds": [
+ "44",
+ "85",
+ "30"
+ ]
+ },
+ {
+ "id": "43",
+ "firstname": "Sarette",
+ "lastname": "Bateson",
+ "email": "sbateson16@163.com",
+ "phone": "497-657-1837",
+ "street": "986 Dennis Way",
+ "city": "Seattle",
+ "relatedContactIds": [
+ "3",
+ "92",
+ "100",
+ "78",
+ "9",
+ "22",
+ "62",
+ "5",
+ "69",
+ "2",
+ "52",
+ "49",
+ "91",
+ "7",
+ "60",
+ "41",
+ "26",
+ "41"
+ ]
+ },
+ {
+ "id": "44",
+ "firstname": "Katha",
+ "lastname": "Seamon",
+ "email": "kseamon17@nationalgeographic.com",
+ "phone": "913-934-5362",
+ "street": "64 Burrows Plaza",
+ "city": "Denver",
+ "relatedContactIds": [
+ "61",
+ "11",
+ "2",
+ "19",
+ "18",
+ "61",
+ "63",
+ "82"
+ ]
+ },
+ {
+ "id": "45",
+ "firstname": "Nanny",
+ "lastname": "Schwand",
+ "email": "nschwand18@blog.com",
+ "phone": "286-800-6111",
+ "street": "978 Butternut Alley",
+ "city": "Denver",
+ "relatedContactIds": [
+ "24",
+ "24",
+ "63",
+ "75",
+ "7",
+ "78",
+ "98",
+ "39",
+ "29",
+ "94"
+ ]
+ },
+ {
+ "id": "46",
+ "firstname": "Prent",
+ "lastname": "Remer",
+ "email": "premer19@pagesperso-orange.fr",
+ "phone": "907-185-4077",
+ "street": "77 Scott Way",
+ "city": "Denver",
+ "relatedContactIds": [
+ "94",
+ "55"
+ ]
+ },
+ {
+ "id": "47",
+ "firstname": "Brendon",
+ "lastname": "Bickerton",
+ "email": "bbickerton1a@nhs.uk",
+ "phone": "709-973-0138",
+ "street": "0494 Scoville Street",
+ "city": "Denver",
+ "relatedContactIds": [
+ "17",
+ "92",
+ "6",
+ "27",
+ "21",
+ "24",
+ "51",
+ "83",
+ "43",
+ "81",
+ "61",
+ "93",
+ "60",
+ "61",
+ "75",
+ "68",
+ "61",
+ "93"
+ ]
+ },
+ {
+ "id": "48",
+ "firstname": "Cyndia",
+ "lastname": "Christofides",
+ "email": "cchristofides1b@slashdot.org",
+ "phone": "920-603-4653",
+ "street": "0 Briar Crest Circle",
+ "city": "Denver",
+ "relatedContactIds": [
+ "47",
+ "20",
+ "58",
+ "56",
+ "58",
+ "92",
+ "48",
+ "92",
+ "92",
+ "94",
+ "29",
+ "70",
+ "39",
+ "62",
+ "64",
+ "35",
+ "77",
+ "55"
+ ]
+ },
+ {
+ "id": "49",
+ "firstname": "Lyon",
+ "lastname": "Vannoort",
+ "email": "lvannoort1c@istockphoto.com",
+ "phone": "821-630-4226",
+ "street": "57 Colorado Parkway",
+ "city": "Denver",
+ "relatedContactIds": [
+ "88",
+ "52",
+ "53",
+ "83",
+ "92",
+ "62",
+ "60",
+ "56",
+ "54",
+ "22"
+ ]
+ },
+ {
+ "id": "50",
+ "firstname": "Riane",
+ "lastname": "Peniman",
+ "email": "rpeniman1d@google.ru",
+ "phone": "473-151-6582",
+ "street": "6 Harbort Road",
+ "city": "Washington",
+ "relatedContactIds": [
+ "26",
+ "57",
+ "1"
+ ]
+ },
+ {
+ "id": "51",
+ "firstname": "Sidoney",
+ "lastname": "Ainscow",
+ "email": "sainscow1e@mayoclinic.com",
+ "phone": "981-848-5913",
+ "street": "47 Vernon Hill",
+ "city": "Washington",
+ "relatedContactIds": [
+ "79",
+ "68",
+ "54",
+ "62",
+ "24",
+ "36",
+ "28",
+ "92",
+ "84",
+ "41"
+ ]
+ },
+ {
+ "id": "52",
+ "firstname": "Jaime",
+ "lastname": "Rastall",
+ "email": "jrastall1f@infoseek.co.jp",
+ "phone": "129-755-7965",
+ "street": "8 Di Loreto Plaza",
+ "city": "Washington",
+ "relatedContactIds": [
+ "10",
+ "28",
+ "71",
+ "10",
+ "87",
+ "79",
+ "66",
+ "3",
+ "71",
+ "27",
+ "19",
+ "49",
+ "18",
+ "40",
+ "52",
+ "69",
+ "70",
+ "82",
+ "90",
+ "55",
+ "29"
+ ]
+ },
+ {
+ "id": "53",
+ "firstname": "Cliff",
+ "lastname": "Bowmer",
+ "email": "cbowmer1g@google.it",
+ "phone": "670-665-7680",
+ "street": "8820 Charing Cross Drive",
+ "city": "Washington",
+ "relatedContactIds": [
+ "66",
+ "58",
+ "63",
+ "7",
+ "46",
+ "64",
+ "36",
+ "56",
+ "20",
+ "28",
+ "78",
+ "94",
+ "2",
+ "28",
+ "96"
+ ]
+ },
+ {
+ "id": "54",
+ "firstname": "Alli",
+ "lastname": "Astlet",
+ "email": "aastlet1h@google.com.br",
+ "phone": "690-185-6023",
+ "street": "37 Kropf Point",
+ "city": "Washington",
+ "relatedContactIds": [
+ "88",
+ "43",
+ "80",
+ "30",
+ "35",
+ "30",
+ "79",
+ "10",
+ "79"
+ ]
+ },
+ {
+ "id": "55",
+ "firstname": "Stacia",
+ "lastname": "Aspinell",
+ "email": "saspinell1i@netscape.com",
+ "phone": "690-252-1053",
+ "street": "37983 Vernon Place",
+ "city": "Boston",
+ "relatedContactIds": [
+ "78",
+ "19",
+ "36",
+ "51",
+ "21",
+ "52",
+ "47",
+ "28",
+ "66",
+ "80",
+ "26",
+ "17",
+ "27",
+ "59",
+ "90",
+ "60",
+ "94",
+ "33",
+ "23"
+ ]
+ },
+ {
+ "id": "56",
+ "firstname": "Cristal",
+ "lastname": "Heining",
+ "email": "cheining1j@apache.org",
+ "phone": "652-391-3748",
+ "street": "8979 Arapahoe Plaza",
+ "city": "Boston",
+ "relatedContactIds": [
+ "62",
+ "41",
+ "24",
+ "69",
+ "40",
+ "52"
+ ]
+ },
+ {
+ "id": "57",
+ "firstname": "Urbain",
+ "lastname": "Robelet",
+ "email": "urobelet1k@amazon.com",
+ "phone": "302-990-8314",
+ "street": "2 Lotheville Junction",
+ "city": "Boston",
+ "relatedContactIds": [
+ "87",
+ "7",
+ "2",
+ "99",
+ "39",
+ "76",
+ "92",
+ "48",
+ "9",
+ "59",
+ "42",
+ "6",
+ "58",
+ "41",
+ "77",
+ "96",
+ "25",
+ "33",
+ "44",
+ "24",
+ "19",
+ "73",
+ "32",
+ "49",
+ "47",
+ "66",
+ "67",
+ "8"
+ ]
+ },
+ {
+ "id": "58",
+ "firstname": "Grete",
+ "lastname": "Powton",
+ "email": "gpowton1l@360.cn",
+ "phone": "989-259-5750",
+ "street": "5784 Sugar Court",
+ "city": "Boston",
+ "relatedContactIds": [
+ "1",
+ "13",
+ "21",
+ "9",
+ "88",
+ "38",
+ "9",
+ "80",
+ "74",
+ "46",
+ "88",
+ "16",
+ "34",
+ "2",
+ "27",
+ "18",
+ "1",
+ "69",
+ "40",
+ "66",
+ "70",
+ "51",
+ "60",
+ "98",
+ "56",
+ "21",
+ "15",
+ "66",
+ "3",
+ "96"
+ ]
+ },
+ {
+ "id": "59",
+ "firstname": "Bernadine",
+ "lastname": "Antonoczyk",
+ "email": "bantonoczyk1m@csmonitor.com",
+ "phone": "635-323-8492",
+ "street": "8056 Basil Crossing",
+ "city": "Boston",
+ "relatedContactIds": [
+ "55",
+ "28",
+ "77",
+ "86",
+ "24"
+ ]
+ },
+ {
+ "id": "60",
+ "firstname": "Carlyn",
+ "lastname": "Leroux",
+ "email": "cleroux1n@discovery.com",
+ "phone": "155-727-0433",
+ "street": "39 Cordelia Center",
+ "city": "Boston",
+ "relatedContactIds": [
+ "52",
+ "83",
+ "9",
+ "89",
+ "6",
+ "86",
+ "35",
+ "77",
+ "79",
+ "15",
+ "60",
+ "1",
+ "28",
+ "66",
+ "62",
+ "96",
+ "95",
+ "76",
+ "48",
+ "2"
+ ]
+ },
+ {
+ "id": "61",
+ "firstname": "Larina",
+ "lastname": "Southouse",
+ "email": "lsouthouse1o@symantec.com",
+ "phone": "144-170-9302",
+ "street": "58735 Fuller Park",
+ "city": "Boston",
+ "relatedContactIds": [
+ "16",
+ "51",
+ "46",
+ "95",
+ "37",
+ "56",
+ "50",
+ "30",
+ "69",
+ "77",
+ "77",
+ "87",
+ "82",
+ "24",
+ "78",
+ "88",
+ "14",
+ "99",
+ "86"
+ ]
+ },
+ {
+ "id": "62",
+ "firstname": "Yvon",
+ "lastname": "Zorer",
+ "email": "yzorer1p@sogou.com",
+ "phone": "339-521-0522",
+ "street": "8 Hintze Road",
+ "city": "Boston",
+ "relatedContactIds": [
+ "30",
+ "49",
+ "3",
+ "52",
+ "76",
+ "88",
+ "6",
+ "4",
+ "43",
+ "71",
+ "5",
+ "29",
+ "79",
+ "88",
+ "84",
+ "70",
+ "98",
+ "65",
+ "40",
+ "45",
+ "81",
+ "66",
+ "60",
+ "81",
+ "79",
+ "90",
+ "1"
+ ]
+ },
+ {
+ "id": "63",
+ "firstname": "Salome",
+ "lastname": "Edscer",
+ "email": "sedscer1q@networkadvertising.org",
+ "phone": "189-468-3364",
+ "street": "435 Buell Lane",
+ "city": "Boston",
+ "relatedContactIds": [
+ "47",
+ "2",
+ "60",
+ "88",
+ "54",
+ "97",
+ "15",
+ "38",
+ "28",
+ "94",
+ "64",
+ "58",
+ "53",
+ "12",
+ "77",
+ "37",
+ "3",
+ "74",
+ "28",
+ "5"
+ ]
+ },
+ {
+ "id": "64",
+ "firstname": "Raynor",
+ "lastname": "Souter",
+ "email": "rsouter1r@linkedin.com",
+ "phone": "606-446-3047",
+ "street": "1372 Forest Court",
+ "city": "Boston",
+ "relatedContactIds": [
+ "82",
+ "32",
+ "80",
+ "14",
+ "6",
+ "60",
+ "38",
+ "85",
+ "20",
+ "94",
+ "4",
+ "2",
+ "44",
+ "89",
+ "33",
+ "57",
+ "85"
+ ]
+ },
+ {
+ "id": "65",
+ "firstname": "Orson",
+ "lastname": "Gross",
+ "email": "ogross1s@dmoz.org",
+ "phone": "218-852-0070",
+ "street": "54 Burning Wood Circle",
+ "city": "Boston",
+ "relatedContactIds": [
+ "62",
+ "57",
+ "40",
+ "38",
+ "93",
+ "78",
+ "4",
+ "78"
+ ]
+ },
+ {
+ "id": "66",
+ "firstname": "Biddie",
+ "lastname": "Bestwall",
+ "email": "bbestwall1t@blogspot.com",
+ "phone": "355-359-3510",
+ "street": "2681 Hanover Park",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "20",
+ "39",
+ "6",
+ "8",
+ "36",
+ "14",
+ "51",
+ "75",
+ "16",
+ "23",
+ "32"
+ ]
+ },
+ {
+ "id": "67",
+ "firstname": "Yul",
+ "lastname": "Hakonsen",
+ "email": "yhakonsen1u@booking.com",
+ "phone": "824-179-8742",
+ "street": "74332 Schurz Circle",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "86",
+ "79",
+ "38",
+ "1",
+ "71",
+ "61"
+ ]
+ },
+ {
+ "id": "68",
+ "firstname": "Dorice",
+ "lastname": "Kynvin",
+ "email": "dkynvin1v@microsoft.com",
+ "phone": "299-153-8252",
+ "street": "02823 Brickson Park Pass",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "98",
+ "77",
+ "18",
+ "39",
+ "3",
+ "92",
+ "19",
+ "92",
+ "96",
+ "68",
+ "28",
+ "68",
+ "70",
+ "57",
+ "20",
+ "42",
+ "59",
+ "4",
+ "37"
+ ]
+ },
+ {
+ "id": "69",
+ "firstname": "Angus",
+ "lastname": "Berge",
+ "email": "aberge1w@amazon.de",
+ "phone": "882-892-3642",
+ "street": "4 Scoville Terrace",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "11",
+ "75",
+ "12",
+ "32",
+ "47",
+ "92",
+ "90",
+ "50",
+ "86",
+ "55",
+ "64",
+ "69",
+ "97",
+ "84",
+ "66",
+ "64",
+ "18",
+ "2",
+ "39",
+ "4",
+ "97"
+ ]
+ },
+ {
+ "id": "70",
+ "firstname": "Noellyn",
+ "lastname": "Mercer",
+ "email": "nmercer1x@bizjournals.com",
+ "phone": "587-521-2523",
+ "street": "4856 Declaration Terrace",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "65",
+ "18",
+ "58",
+ "92",
+ "47",
+ "64",
+ "4",
+ "88",
+ "48",
+ "47",
+ "31",
+ "73",
+ "70",
+ "63",
+ "9",
+ "54",
+ "6",
+ "46",
+ "91",
+ "61",
+ "89"
+ ]
+ },
+ {
+ "id": "71",
+ "firstname": "Jarred",
+ "lastname": "Loader",
+ "email": "jloader1y@skype.com",
+ "phone": "167-538-7946",
+ "street": "12 Warbler Way",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "4",
+ "72",
+ "60",
+ "68",
+ "77",
+ "2",
+ "35",
+ "4",
+ "99",
+ "98",
+ "31",
+ "16",
+ "72",
+ "10",
+ "88",
+ "4",
+ "74",
+ "74",
+ "65",
+ "70",
+ "78",
+ "21",
+ "93",
+ "54"
+ ]
+ },
+ {
+ "id": "72",
+ "firstname": "Etan",
+ "lastname": "Abate",
+ "email": "eabate1z@google.pl",
+ "phone": "183-777-0106",
+ "street": "14 Vernon Alley",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "10",
+ "88",
+ "95",
+ "72",
+ "39",
+ "56",
+ "5",
+ "24",
+ "49",
+ "85",
+ "17",
+ "6",
+ "4",
+ "27",
+ "24",
+ "13",
+ "5",
+ "71"
+ ]
+ },
+ {
+ "id": "73",
+ "firstname": "Ella",
+ "lastname": "Burnham",
+ "email": "eburnham20@google.ca",
+ "phone": "266-930-2209",
+ "street": "8588 Maywood Road",
+ "city": "Nashville",
+ "relatedContactIds": [
+ "99",
+ "55",
+ "45",
+ "25",
+ "48",
+ "21",
+ "51",
+ "94",
+ "39",
+ "72",
+ "56",
+ "50"
+ ]
+ },
+ {
+ "id": "74",
+ "firstname": "Enrique",
+ "lastname": "Moyce",
+ "email": "emoyce21@furl.net",
+ "phone": "338-751-0068",
+ "street": "5883 Corscot Pass",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "61",
+ "18",
+ "100",
+ "43",
+ "29",
+ "9",
+ "80",
+ "41",
+ "11",
+ "54",
+ "75",
+ "27",
+ "34",
+ "79",
+ "30",
+ "56",
+ "66",
+ "59",
+ "70",
+ "11",
+ "96",
+ "55",
+ "67",
+ "60",
+ "48",
+ "86",
+ "75",
+ "56",
+ "90",
+ "10"
+ ]
+ },
+ {
+ "id": "75",
+ "firstname": "Alvin",
+ "lastname": "Zambon",
+ "email": "azambon22@mozilla.com",
+ "phone": "551-439-1644",
+ "street": "37947 Gateway Way",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "91",
+ "87",
+ "3",
+ "68",
+ "87",
+ "78",
+ "58",
+ "66",
+ "30",
+ "35",
+ "87",
+ "39",
+ "8",
+ "81",
+ "60",
+ "87",
+ "56",
+ "91",
+ "31",
+ "89",
+ "75",
+ "28",
+ "71",
+ "43"
+ ]
+ },
+ {
+ "id": "76",
+ "firstname": "Kristian",
+ "lastname": "Coxhead",
+ "email": "kcoxhead23@bing.com",
+ "phone": "479-956-9226",
+ "street": "2 Tennyson Circle",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "8",
+ "95",
+ "86",
+ "75",
+ "8",
+ "49",
+ "64",
+ "71",
+ "31",
+ "90",
+ "19",
+ "11",
+ "54",
+ "54",
+ "30",
+ "47",
+ "6",
+ "24",
+ "19",
+ "75"
+ ]
+ },
+ {
+ "id": "77",
+ "firstname": "Pancho",
+ "lastname": "Woloschinski",
+ "email": "pwoloschinski24@hhs.gov",
+ "phone": "842-799-5251",
+ "street": "78 Calypso Pass",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "81",
+ "23",
+ "10",
+ "76",
+ "38",
+ "26",
+ "56",
+ "45",
+ "40",
+ "20",
+ "92",
+ "3",
+ "83",
+ "39",
+ "19",
+ "42",
+ "44",
+ "53",
+ "10",
+ "77",
+ "68",
+ "45",
+ "3",
+ "33"
+ ]
+ },
+ {
+ "id": "78",
+ "firstname": "Berti",
+ "lastname": "Wolfers",
+ "email": "bwolfers25@about.me",
+ "phone": "158-232-4876",
+ "street": "6105 Loomis Road",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "8",
+ "81",
+ "76",
+ "63",
+ "68",
+ "30",
+ "1",
+ "40",
+ "95",
+ "47",
+ "49",
+ "12",
+ "7"
+ ]
+ },
+ {
+ "id": "79",
+ "firstname": "Shelbi",
+ "lastname": "Gartan",
+ "email": "sgartan26@canalblog.com",
+ "phone": "270-316-1250",
+ "street": "384 Tomscot Hill",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "81",
+ "18",
+ "90",
+ "13",
+ "75",
+ "75",
+ "99",
+ "4",
+ "18",
+ "72",
+ "84",
+ "8",
+ "55"
+ ]
+ },
+ {
+ "id": "80",
+ "firstname": "Cristi",
+ "lastname": "Kull",
+ "email": "ckull27@elpais.com",
+ "phone": "158-767-7757",
+ "street": "93468 Spaight Trail",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "30",
+ "26",
+ "31",
+ "84",
+ "100",
+ "21",
+ "38",
+ "58",
+ "69",
+ "42",
+ "53",
+ "64",
+ "33",
+ "71",
+ "54",
+ "82",
+ "92",
+ "70",
+ "49",
+ "43",
+ "14",
+ "53",
+ "31",
+ "54",
+ "46",
+ "73",
+ "82",
+ "78"
+ ]
+ },
+ {
+ "id": "81",
+ "firstname": "Laurent",
+ "lastname": "Le Count",
+ "email": "llecount28@lycos.com",
+ "phone": "366-236-8102",
+ "street": "1 North Terrace",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "45",
+ "7"
+ ]
+ },
+ {
+ "id": "82",
+ "firstname": "Lodovico",
+ "lastname": "Benezet",
+ "email": "lbenezet29@dailymotion.com",
+ "phone": "258-407-0659",
+ "street": "06769 Dixon Center",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "27",
+ "4",
+ "3",
+ "18",
+ "15",
+ "90",
+ "57",
+ "57",
+ "49",
+ "83",
+ "16",
+ "44",
+ "97",
+ "42",
+ "56",
+ "4",
+ "40"
+ ]
+ },
+ {
+ "id": "83",
+ "firstname": "Beverlie",
+ "lastname": "Hollyman",
+ "email": "bhollyman2a@webs.com",
+ "phone": "684-917-1551",
+ "street": "6709 Westend Road",
+ "city": "Baltimore",
+ "relatedContactIds": [
+ "43",
+ "92",
+ "44"
+ ]
+ },
+ {
+ "id": "84",
+ "firstname": "Bronny",
+ "lastname": "Shalloo",
+ "email": "bshalloo2b@newyorker.com",
+ "phone": "897-305-8712",
+ "street": "234 Grayhawk Trail",
+ "city": "Oklahoma City",
+ "relatedContactIds": [
+ "24",
+ "84",
+ "45",
+ "14",
+ "32",
+ "39",
+ "19",
+ "53",
+ "98",
+ "34",
+ "73",
+ "95",
+ "15",
+ "23",
+ "52",
+ "88",
+ "87",
+ "52",
+ "76"
+ ]
+ },
+ {
+ "id": "85",
+ "firstname": "Ashien",
+ "lastname": "Wittleton",
+ "email": "awittleton2c@statcounter.com",
+ "phone": "593-414-1162",
+ "street": "471 Twin Pines Point",
+ "city": "Oklahoma City",
+ "relatedContactIds": [
+ "18",
+ "88",
+ "42"
+ ]
+ },
+ {
+ "id": "86",
+ "firstname": "Datha",
+ "lastname": "Stuttman",
+ "email": "dstuttman2d@apache.org",
+ "phone": "274-213-4881",
+ "street": "5667 Bonner Drive",
+ "city": "Oklahoma City",
+ "relatedContactIds": [
+ "36",
+ "9"
+ ]
+ },
+ {
+ "id": "87",
+ "firstname": "Averell",
+ "lastname": "Whates",
+ "email": "awhates2e@pcworld.com",
+ "phone": "901-509-3869",
+ "street": "21 Dottie Crossing",
+ "city": "Oklahoma City",
+ "relatedContactIds": [
+ "42",
+ "29",
+ "43",
+ "17",
+ "67",
+ "59",
+ "10",
+ "63",
+ "10",
+ "16",
+ "34",
+ "43",
+ "76",
+ "61",
+ "92",
+ "46",
+ "56",
+ "65",
+ "52",
+ "33",
+ "45",
+ "30",
+ "84",
+ "42",
+ "18",
+ "12",
+ "6",
+ "44",
+ "34"
+ ]
+ },
+ {
+ "id": "88",
+ "firstname": "Katharine",
+ "lastname": "Kassel",
+ "email": "kkassel2f@google.ca",
+ "phone": "179-877-5021",
+ "street": "25 Moose Street",
+ "city": "Oklahoma City",
+ "relatedContactIds": [
+ "66",
+ "64",
+ "49",
+ "26",
+ "23",
+ "22",
+ "24",
+ "58",
+ "96",
+ "98",
+ "39",
+ "11",
+ "22",
+ "87"
+ ]
+ },
+ {
+ "id": "89",
+ "firstname": "Kingsley",
+ "lastname": "Pfaffel",
+ "email": "kpfaffel2g@amazonaws.com",
+ "phone": "423-747-9306",
+ "street": "5 Crest Line Circle",
+ "city": "Oklahoma City",
+ "relatedContactIds": [
+ "34",
+ "23",
+ "87",
+ "22",
+ "16",
+ "51",
+ "69",
+ "5",
+ "54",
+ "56",
+ "12",
+ "92",
+ "51",
+ "77",
+ "33",
+ "90",
+ "32",
+ "6",
+ "42",
+ "7",
+ "25",
+ "8",
+ "77",
+ "75",
+ "88",
+ "86",
+ "98"
+ ]
+ },
+ {
+ "id": "90",
+ "firstname": "Hobart",
+ "lastname": "Tosh",
+ "email": "htosh2h@tmall.com",
+ "phone": "760-935-3287",
+ "street": "95772 6th Junction",
+ "city": "Portland",
+ "relatedContactIds": [
+ "52",
+ "41",
+ "87",
+ "25",
+ "39",
+ "10",
+ "12",
+ "13",
+ "65",
+ "92"
+ ]
+ },
+ {
+ "id": "91",
+ "firstname": "Marlon",
+ "lastname": "Curm",
+ "email": "mcurm2i@nytimes.com",
+ "phone": "821-689-5277",
+ "street": "9257 Upham Drive",
+ "city": "Portland",
+ "relatedContactIds": [
+ "94",
+ "88",
+ "83",
+ "23",
+ "18",
+ "32",
+ "77",
+ "44",
+ "17",
+ "94",
+ "9",
+ "78",
+ "40",
+ "70",
+ "6",
+ "86",
+ "77",
+ "67",
+ "81",
+ "71"
+ ]
+ },
+ {
+ "id": "92",
+ "firstname": "Ax",
+ "lastname": "Medler",
+ "email": "amedler2j@cdc.gov",
+ "phone": "292-637-3497",
+ "street": "5 Spaight Center",
+ "city": "Portland",
+ "relatedContactIds": [
+ "88",
+ "45",
+ "6",
+ "84",
+ "59",
+ "31",
+ "3",
+ "40",
+ "100",
+ "81",
+ "79",
+ "42",
+ "100",
+ "16",
+ "23",
+ "79",
+ "24",
+ "51",
+ "17",
+ "71",
+ "70",
+ "4",
+ "88",
+ "38",
+ "81"
+ ]
+ },
+ {
+ "id": "93",
+ "firstname": "Jehanna",
+ "lastname": "Groom",
+ "email": "jgroom2k@rediff.com",
+ "phone": "805-530-8235",
+ "street": "8 Scott Junction",
+ "city": "Portland",
+ "relatedContactIds": [
+ "58",
+ "87",
+ "30",
+ "24",
+ "74",
+ "74",
+ "39",
+ "2",
+ "91",
+ "24",
+ "43",
+ "32",
+ "20",
+ "23",
+ "40"
+ ]
+ },
+ {
+ "id": "94",
+ "firstname": "Jerry",
+ "lastname": "Khristyukhin",
+ "email": "jkhristyukhin2l@cocolog-nifty.com",
+ "phone": "341-952-1933",
+ "street": "20605 Ramsey Avenue",
+ "city": "Portland",
+ "relatedContactIds": [
+ "8",
+ "27",
+ "70",
+ "55",
+ "2",
+ "79",
+ "22",
+ "2",
+ "71",
+ "72",
+ "35",
+ "81",
+ "49",
+ "55",
+ "38",
+ "46",
+ "93",
+ "69",
+ "46",
+ "25",
+ "98",
+ "97",
+ "39",
+ "15",
+ "2",
+ "70",
+ "4",
+ "54",
+ "43",
+ "73"
+ ]
+ },
+ {
+ "id": "95",
+ "firstname": "Germaine",
+ "lastname": "Sumsion",
+ "email": "gsumsion2m@timesonline.co.uk",
+ "phone": "939-932-7402",
+ "street": "0646 Sage Trail",
+ "city": "Portland",
+ "relatedContactIds": [
+ "1",
+ "90",
+ "68",
+ "35",
+ "68",
+ "94",
+ "11",
+ "81",
+ "54",
+ "57",
+ "53",
+ "15",
+ "62",
+ "93",
+ "47",
+ "67",
+ "15",
+ "47",
+ "83",
+ "6",
+ "14",
+ "96",
+ "83",
+ "20",
+ "88",
+ "100",
+ "19",
+ "31",
+ "54"
+ ]
+ },
+ {
+ "id": "96",
+ "firstname": "Florri",
+ "lastname": "Brunke",
+ "email": "fbrunke2n@ezinearticles.com",
+ "phone": "445-575-4372",
+ "street": "0502 Veith Avenue",
+ "city": "Las Vegas",
+ "relatedContactIds": [
+ "58",
+ "84",
+ "51",
+ "65",
+ "50",
+ "60",
+ "81",
+ "10",
+ "70",
+ "72",
+ "31",
+ "63",
+ "16",
+ "20",
+ "62",
+ "72",
+ "85",
+ "81",
+ "27",
+ "34",
+ "54",
+ "79",
+ "98",
+ "75",
+ "71"
+ ]
+ },
+ {
+ "id": "97",
+ "firstname": "Gonzalo",
+ "lastname": "Aland",
+ "email": "galand2o@washington.edu",
+ "phone": "742-931-4232",
+ "street": "6022 Lighthouse Bay Terrace",
+ "city": "Las Vegas",
+ "relatedContactIds": [
+ "31"
+ ]
+ },
+ {
+ "id": "98",
+ "firstname": "Birgit",
+ "lastname": "Finnan",
+ "email": "bfinnan2p@cbsnews.com",
+ "phone": "663-848-9691",
+ "street": "63026 Karstens Alley",
+ "city": "Las Vegas",
+ "relatedContactIds": [
+ "84",
+ "91",
+ "68",
+ "22",
+ "11",
+ "76",
+ "100",
+ "95",
+ "1"
+ ]
+ },
+ {
+ "id": "99",
+ "firstname": "Sibyl",
+ "lastname": "McGillacoell",
+ "email": "smcgillacoell2q@tumblr.com",
+ "phone": "444-668-4955",
+ "street": "329 Merrick Hill",
+ "city": "Las Vegas",
+ "relatedContactIds": [
+ "54",
+ "61",
+ "92",
+ "76",
+ "25"
+ ]
+ },
+ {
+ "id": "100",
+ "firstname": "Charmian",
+ "lastname": "McLurg",
+ "email": "cmclurg2r@cnet.com",
+ "phone": "821-884-8382",
+ "street": "52531 Union Court",
+ "city": "Las Vegas",
+ "relatedContactIds": [
+ "15",
+ "8",
+ "46",
+ "19",
+ "72",
+ "24",
+ "94"
+ ]
+ }
+]
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/assets/manifest.json b/projects/e2e/workbench-application-platform/contact-app/src/assets/manifest.json
new file mode 100644
index 000000000..08437d0e9
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/assets/manifest.json
@@ -0,0 +1,78 @@
+{
+ "name": "Contact Application",
+ "baseUrl": "#",
+ "capabilities": [
+ {
+ "type": "activity",
+ "qualifier": {
+ "entity": "contacts"
+ },
+ "description": "Lists all contacts and allows to show their personal data.",
+ "properties": {
+ "title": "Contacts",
+ "itemText": "person_outline",
+ "itemCssClass": "material-icons",
+ "cssClass": "e2e-contact-list",
+ "path": "contact/list"
+ }
+ },
+ {
+ "type": "view",
+ "qualifier": {
+ "entity": "contact",
+ "id": "*"
+ },
+ "description": "Shows personal data of a contact.",
+ "properties": {
+ "path": "contact/:id",
+ "title": "Contact",
+ "cssClass": "e2e-contact"
+ }
+ },
+ {
+ "type": "popup",
+ "qualifier": {
+ "entity": "contact",
+ "action": "create"
+ },
+ "description": "Allows to create a new contact.",
+ "properties": {
+ "path": "contact/new",
+ "width": "400px",
+ "height": "390px"
+ }
+ },
+ {
+ "type": "popup",
+ "qualifier": {
+ "entity": "contact",
+ "id": "*",
+ "action": "add-related-contact"
+ },
+ "description": "Allows to add another contact as related person.",
+ "properties": {
+ "path": "contact/:id/add-related-contact",
+ "width": "350px",
+ "height": "600px"
+ }
+ }
+ ],
+ "intents": [
+ {
+ "type": "view",
+ "qualifier": {
+ "entity": "communication",
+ "presentation": "list",
+ "contactId": "*"
+ }
+ },
+ {
+ "type": "popup",
+ "qualifier": {
+ "entity": "communication",
+ "action": "create",
+ "contactId": "*"
+ }
+ }
+ ]
+}
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/environments/environment.now.ts b/projects/e2e/workbench-application-platform/contact-app/src/environments/environment.now.ts
new file mode 100644
index 000000000..dc39aca58
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/environments/environment.now.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+export const environment = {
+ production: true
+};
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/environments/environment.ts b/projects/e2e/workbench-application-platform/contact-app/src/environments/environment.ts
new file mode 100644
index 000000000..2a785475f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/environments/environment.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --configuration=now` replaces `environment.ts` with `environment.now.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/index.html b/projects/e2e/workbench-application-platform/contact-app/src/index.html
new file mode 100644
index 000000000..442f7103a
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Contact Application
+
+
+
+
+
+
+
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/main.ts b/projects/e2e/workbench-application-platform/contact-app/src/main.ts
new file mode 100644
index 000000000..c7b673cf4
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/now.json b/projects/e2e/workbench-application-platform/contact-app/src/now.json
new file mode 100644
index 000000000..7f9d9a3eb
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/now.json
@@ -0,0 +1,5 @@
+{
+ "version": 2,
+ "name": "scion-workbench-application-platform-contact",
+ "alias": "scion-workbench-application-platform-contact"
+}
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/polyfills.ts b/projects/e2e/workbench-application-platform/contact-app/src/polyfills.ts
new file mode 100644
index 000000000..f56cc3de6
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/polyfills.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/weak-map';
+import 'core-js/es6/set';
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+/**
+ * If the application will be indexed by Google Search, the following is required.
+ * Googlebot uses a renderer based on Chrome 41.
+ * https://developers.google.com/search/docs/guides/rendering
+ **/
+// import 'core-js/es6/array';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect';
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ */
+
+ // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+
+ /*
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ */
+// (window as any).__Zone_enable_cross_context_check = true;
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/projects/e2e/workbench-application-platform/contact-app/src/styles.scss b/projects/e2e/workbench-application-platform/contact-app/src/styles.scss
new file mode 100644
index 000000000..1506f1627
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/src/styles.scss
@@ -0,0 +1,3 @@
+@import 'common';
+
+@include app-theme();
diff --git a/projects/e2e/workbench-application-platform/contact-app/tsconfig.app.json b/projects/e2e/workbench-application-platform/contact-app/tsconfig.app.json
new file mode 100644
index 000000000..0bc50df29
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/tsconfig.app.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../../out-tsc/contact-app",
+ "types": []
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/contact-app/tslint.json b/projects/e2e/workbench-application-platform/contact-app/tslint.json
new file mode 100644
index 000000000..e3e7b64d8
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/contact-app/tslint.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../../../tslint.json",
+ "rules": {
+ "directive-selector": [
+ true,
+ "attribute",
+ "app",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "app",
+ "kebab-case"
+ ]
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/browserslist b/projects/e2e/workbench-application-platform/dev-tools-app/browserslist
new file mode 100644
index 000000000..37371cb04
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/browserslist
@@ -0,0 +1,11 @@
+# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+#
+# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
+
+> 0.5%
+last 2 versions
+Firefox ESR
+not dead
+not IE 9-11
\ No newline at end of file
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app-routing.module.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app-routing.module.ts
new file mode 100644
index 000000000..afa3ac21e
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app-routing.module.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+ {path: 'dev-tools', loadChildren: './dev-tools/dev-tools.module#DevToolsModule'},
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes, {useHash: true})],
+ exports: [RouterModule]
+})
+export class AppRoutingModule {
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.html
new file mode 100644
index 000000000..8fe45de6e
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.scss
new file mode 100644
index 000000000..e1f2859cf
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.scss
@@ -0,0 +1,13 @@
+@import 'common';
+
+:host {
+ @include position(absolute, 0, 0, 0, 0);
+
+ > sci-viewport {
+ @include position(absolute, 0, 0, 0, 0);
+
+ router-outlet {
+ position: absolute; // take router outlet out of the document flow
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.ts
new file mode 100644
index 000000000..15f0590f2
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.component.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss']
+})
+export class AppComponent {
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.module.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.module.ts
new file mode 100644
index 000000000..49f5b608e
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/app.module.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+
+import { AppComponent } from './app.component';
+import { WorkbenchApplicationModule } from '@scion/workbench-application.angular';
+import { SciViewportModule } from '@scion/viewport';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { AppRoutingModule } from './app-routing.module';
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ],
+ imports: [
+ BrowserModule,
+ WorkbenchApplicationModule.forRoot(),
+ SciViewportModule,
+ AppRoutingModule,
+ BrowserAnimationsModule,
+ ],
+ providers: [],
+ bootstrap: [
+ AppComponent,
+ ]
+})
+export class AppModule {
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.html
new file mode 100644
index 000000000..a9a88ee59
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.html
@@ -0,0 +1,8 @@
+{{manifest.name}}
+{{manifest.symbolicName}}
+{{manifest.baseUrl}}
+
+
+ {{intentCount}} {{intentCount === 1 ? 'intent' : 'intents'}}
+ {{capabilityCount}} {{capabilityCount === 1 ? 'capability' : 'capabilities'}}
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.scss
new file mode 100644
index 000000000..d9257d144
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.scss
@@ -0,0 +1,40 @@
+@import 'common';
+
+:host {
+ display: grid;
+ grid-template-columns: auto auto;
+ grid-column-gap: 1em;
+ grid-row-gap: .5em;
+ grid-template-rows: auto;
+ align-items: start;
+
+ > a.app-name {
+ grid-column: 1/2;
+ grid-row: 1/2;
+ font-weight: bold;
+ }
+
+ > span.app-symbolic-name {
+ grid-column: 1/2;
+ grid-row: 2/3;
+ font-size: smaller;
+ }
+
+ > a.url {
+ grid-column: 1/3;
+ grid-row: 3/4;
+ font-size: smaller;
+ }
+
+ > div.chips {
+ grid-column: 2/3;
+ grid-row: 1/3;
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: flex-end;
+
+ > span.chip {
+ @include chip(accentColor(.2), transparent, accentColor(.75));
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.ts
new file mode 100644
index 000000000..8141f6493
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list-item/application-list-item.component.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { Manifest } from '@scion/workbench-application.core';
+
+@Component({
+ selector: 'app-application-list-item',
+ templateUrl: './application-list-item.component.html',
+ styleUrls: ['./application-list-item.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ApplicationListItemComponent {
+
+ @Input()
+ public manifest: Manifest;
+
+ public get intentCount(): number {
+ return this.manifest.intents.length;
+ }
+
+ public get capabilityCount(): number {
+ return this.manifest.capabilities.length;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.html
new file mode 100644
index 000000000..213303c39
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.scss
new file mode 100644
index 000000000..57bda86d3
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.scss
@@ -0,0 +1,10 @@
+@import 'common';
+
+:host {
+ display: flex;
+ padding: app-padding();
+
+ > sci-list {
+ flex: auto;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.ts
new file mode 100644
index 000000000..cfe299814
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-list/application-list.component.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component, TrackByFunction } from '@angular/core';
+import { BehaviorSubject, combineLatest, MonoTypeOperatorFunction, Observable, OperatorFunction } from 'rxjs';
+import { Manifest, ManifestRegistryService } from '@scion/workbench-application.core';
+import { map } from 'rxjs/operators';
+import { toFilterRegExp } from '@scion/e2e/common';
+
+@Component({
+ selector: 'app-application-list',
+ templateUrl: './application-list.component.html',
+ styleUrls: ['./application-list.component.scss'],
+})
+export class ApplicationListComponent {
+
+ private _filter$ = new BehaviorSubject(null);
+
+ public manifests$: Observable;
+
+ constructor(private _manifestRegistryService: ManifestRegistryService) {
+ this.manifests$ = combineLatest(this._filter$, this._manifestRegistryService.manifests$)
+ .pipe(
+ filterManifests(),
+ sortManifests()
+ );
+ }
+
+ public onFilter(filterText: string): void {
+ this._filter$.next(filterText);
+ }
+
+ public trackByFn: TrackByFunction = (index: number, manifest: Manifest): any => {
+ return manifest.symbolicName;
+ };
+}
+
+function filterManifests(): OperatorFunction<[string, Manifest[]], Manifest[]> {
+ return map(([filter, manifests]: [string, Manifest[]]): Manifest[] => {
+ if (!filter) {
+ return manifests;
+ }
+
+ const filterRegExp = toFilterRegExp(filter);
+ return manifests.filter(manifest => filterRegExp.test(manifest.name) || filterRegExp.test(manifest.symbolicName));
+ });
+}
+
+function sortManifests(): MonoTypeOperatorFunction {
+ return map((manifests: Manifest[]): Manifest[] => [...manifests].sort((mf1, mf2) => mf1.name.localeCompare(mf2.name)));
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.html
new file mode 100644
index 000000000..d6eb4e125
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+ Capabilities{{capabilities.length}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Intents{{intents.length}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.scss
new file mode 100644
index 000000000..ddca98d85
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.scss
@@ -0,0 +1,134 @@
+@import 'common';
+
+:host {
+ display: flex;
+ flex-direction: column;
+ padding: app-padding();
+
+ > section.application {
+ flex: none;
+ display: grid;
+ grid-template-columns: minmax(80px, 120px) auto auto;
+ grid-column-gap: 1em;
+ grid-row-gap: .5em;
+ grid-template-rows: auto;
+ margin-bottom: 1.5em;
+
+ border: 1px solid accentColor(.25);
+ border-radius: 5px;
+ padding: 1em;
+ @include grid-container-align-items(center);
+
+ > label.app-name {
+ grid-column: 1/2;
+ grid-row: 1/2;
+ }
+
+ > span.app-name {
+ grid-column: 2/3;
+ grid-row: 1/2;
+ }
+
+ > label.app-symbolic-name {
+ grid-column: 1/2;
+ grid-row: 2/3;
+ }
+
+ > span.app-symbolic-name {
+ grid-column: 2/3;
+ grid-row: 2/3;
+ }
+
+ > label.app-url {
+ grid-column: 1/2;
+ grid-row: 3/4;
+ }
+
+ > a.app-url {
+ grid-column: 2/3;
+ grid-row: 3/4;
+ }
+
+ > label.manifest-url {
+ grid-column: 1/2;
+ grid-row: 4/5;
+ }
+
+ > a.manifest-url {
+ grid-column: 2/3;
+ grid-row: 4/5;
+ }
+
+ > span.scope-check-disabled {
+ grid-column: 3/4;
+ grid-row: 1/5;
+ align-self: start;
+ justify-self: end;
+ font-weight: bold;
+ @include chip(rgba(255, 0, 0, .75), rgba(255, 0, 0, .1), rgba(255, 0, 0, .75));
+ margin: 0;
+ }
+ }
+
+ > section.sashbox {
+ flex: auto;
+ display: flex;
+ flex-direction: column;
+
+ > section.sash-1, > section.sash-2 {
+ flex: 1 1 0;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+
+ > h2 {
+ margin-top: 0;
+ font-size: 1.25em;
+ position: relative; // positioning anchor for the badge
+ align-self: flex-start;
+
+ > span.count-badge {
+ $size: 12px;
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ right: -1.5em;
+ top: 0;
+ border-radius: $size/2;
+ color: #FFFFFF;
+ font-size: 10px;
+ font-weight: normal;
+ min-height: $size;
+ min-width: $size;
+ background-color: accentColor(.4);
+ user-select: none;
+ }
+ }
+
+ > sci-filter-field {
+ margin-bottom: .2em;
+ }
+
+ > sci-accordion {
+ flex: auto;
+ }
+ }
+
+ > div.splitter {
+ flex: 0 0 auto;
+ margin: .5em 0;
+ width: 20px;
+ align-self: center;
+
+ > div.sash-indicator {
+ background: accentColor();
+ height: 1px;
+
+ &:not(:first-child) {
+ margin-top: 1px;
+ }
+ }
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.ts
new file mode 100644
index 000000000..6a38d57fb
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/application-view/application-view.component.ts
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
+import { provideWorkbenchView, WorkbenchView } from '@scion/workbench-application.angular';
+import { ActivatedRoute } from '@angular/router';
+import { BehaviorSubject, combineLatest, Observable, of, OperatorFunction, Subject } from 'rxjs';
+import { distinctUntilChanged, filter, map, switchMap, takeUntil, tap } from 'rxjs/operators';
+import { Capability, Intent, Manifest, ManifestRegistryService, NotificationService } from '@scion/workbench-application.core';
+import { toFilterRegExp } from '@scion/e2e/common';
+
+@Component({
+ selector: 'app-application-view',
+ templateUrl: './application-view.component.html',
+ styleUrls: ['./application-view.component.scss'],
+ providers: [
+ provideWorkbenchView(ApplicationViewComponent)
+ ]
+})
+export class ApplicationViewComponent implements OnDestroy {
+
+ private _destroy$ = new Subject();
+
+ public manifest: Manifest;
+ public capabilities$: Observable;
+ public intents$: Observable;
+
+ public sashPositionFr = .5;
+
+ private _capabilityFilter$ = new BehaviorSubject(null);
+ private _intentFilter$ = new BehaviorSubject(null);
+
+ @ViewChild('sashbox')
+ private _sashbox: ElementRef;
+
+ constructor(route: ActivatedRoute,
+ notificationService: NotificationService,
+ view: WorkbenchView,
+ private _manifestRegistryService: ManifestRegistryService) {
+ route.params
+ .pipe(
+ map(params => params['symbolicName']),
+ distinctUntilChanged(),
+ switchMap(symbolicName => this._manifestRegistryService.manifest$(symbolicName)),
+ tap(manifest => !manifest && notificationService.notify({
+ severity: 'error',
+ title: 'Application not found',
+ text: `No application found with given symbolic name '${route.snapshot.params['symbolicName']}'`
+ })),
+ filter(Boolean),
+ takeUntil(this._destroy$)
+ )
+ .subscribe((manifest: Manifest) => {
+ const capabilities: Capability[] = [...manifest.capabilities].sort((c1, c2) => c1.type.localeCompare(c2.type));
+ const intents: Intent[] = [...manifest.intents].sort((i1, it2) => i1.type.localeCompare(it2.type));
+
+ view.title = manifest.name;
+ view.heading = 'Application Manifest';
+
+ this.manifest = manifest;
+ this.capabilities$ = combineLatest(this._capabilityFilter$, of(capabilities)).pipe(filterCapabilities());
+ this.intents$ = combineLatest(this._intentFilter$, of(intents)).pipe(filterIntents());
+ });
+ }
+
+ public onSash(deltaPx: number): void {
+ const sashboxHeightPx = this._sashbox.nativeElement.clientHeight;
+ const sashPositionFr = this.sashPositionFr + (deltaPx / sashboxHeightPx);
+ this.sashPositionFr = Math.min(1, Math.max(0, sashPositionFr));
+ }
+
+ public onCapabilityFilter(filterText: string): void {
+ this._capabilityFilter$.next(filterText);
+ }
+
+ public onIntentFilter(filterText: string): void {
+ this._intentFilter$.next(filterText);
+ }
+
+ public onSashReset(): void {
+ this.sashPositionFr = .5;
+ }
+
+ public ngOnDestroy(): void {
+ this._destroy$.next();
+ }
+}
+
+function filterCapabilities(): OperatorFunction<[string, Capability[]], Capability[]> {
+ return map(([filterText, capabilities]: [string, Capability[]]): Capability[] => {
+ if (!filterText) {
+ return capabilities;
+ }
+
+ const filterRegExp = toFilterRegExp(filterText);
+ return capabilities.filter(capability => (
+ filterRegExp.test(capability.type) ||
+ filterRegExp.test(capability.private ? 'private' : 'public') ||
+ Object.keys(capability.qualifier || {}).some(key => filterRegExp.test(key)) ||
+ Object.values(capability.qualifier || {}).some(value => filterRegExp.test(`${value}`)))
+ );
+ });
+}
+
+function filterIntents(): OperatorFunction<[string, Intent[]], Intent[]> {
+ return map(([filterText, intents]: [string, Intent[]]): Intent[] => {
+ if (!filterText) {
+ return intents;
+ }
+
+ const filterRegExp = toFilterRegExp(filterText);
+ return intents.filter(intent => (
+ filterRegExp.test(intent.type) ||
+ filterRegExp.test(intent.metadata.implicit ? 'implicit' : 'explicit') ||
+ Object.keys(intent.qualifier || {}).some(key => filterRegExp.test(key)) ||
+ Object.values(intent.qualifier || {}).some(value => filterRegExp.test(`${value}`)))
+ );
+ });
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.html
new file mode 100644
index 000000000..28f358f0a
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.html
@@ -0,0 +1,16 @@
+
+
+
+ PUBLIC
+
+
+ PRIVATE
+
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.scss
new file mode 100644
index 000000000..8a0adc136
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.scss
@@ -0,0 +1,28 @@
+@import 'common';
+
+:host {
+ display: grid;
+ justify-content: space-between;
+ @include grid-container-align-items(center);
+ grid-template-columns: auto auto;
+ grid-template-rows: auto;
+ grid-column-gap: 1em;
+
+ > div.actions {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+
+ > span.scope {
+ font-weight: bold;
+ @include chip(primaryColor(), null, primaryColor());
+ margin: 0;
+ }
+
+ > button.execute {
+ &:not(:first-child) {
+ margin-top: .5em;
+ }
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.ts
new file mode 100644
index 000000000..44230022c
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-item/capability-accordion-item.component.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
+import { Capability, ManifestRegistryService, PlatformCapabilityTypes, Popup, PopupService, Qualifier } from '@scion/workbench-application.core';
+import { map, switchMap, takeUntil } from 'rxjs/operators';
+import { Subject } from 'rxjs';
+
+@Component({
+ selector: 'app-capability-accordion-item',
+ templateUrl: './capability-accordion-item.component.html',
+ styleUrls: ['./capability-accordion-item.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CapabilityAccordionItemComponent implements OnChanges, OnDestroy {
+
+ private _destroy$ = new Subject();
+ private _inputChange$ = new Subject();
+
+ public execQualifier: Qualifier;
+
+ @Input()
+ public capability: Capability;
+
+ constructor(private _popupService: PopupService,
+ manifestRegistryService: ManifestRegistryService,
+ cd: ChangeDetectorRef) {
+ this._inputChange$
+ .pipe(
+ switchMap(() => manifestRegistryService.capabilities$(PlatformCapabilityTypes.Popup, this.createExecQualifier())),
+ map((capabilities: Capability[]) => capabilities.length > 0),
+ takeUntil(this._destroy$),
+ )
+ .subscribe((executable: boolean) => {
+ this.execQualifier = (executable ? this.createExecQualifier() : null);
+ cd.markForCheck();
+ });
+ }
+
+ public ngOnChanges(changes: SimpleChanges): void {
+ this._inputChange$.next();
+ }
+
+ public onCapabilityExecute(event: MouseEvent): void {
+ if (!this.execQualifier) {
+ return;
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ const popup: Popup = {
+ position: 'west',
+ anchor: event.target as Element,
+ };
+
+ this._popupService.open(popup, this.execQualifier);
+ }
+
+ private createExecQualifier(): Qualifier {
+ return {
+ entity: 'capability',
+ action: 'execute',
+ type: this.capability.type,
+ capabilityId: this.capability.metadata.id,
+ };
+ }
+
+ public ngOnDestroy(): void {
+ this._destroy$.next();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.html
new file mode 100644
index 000000000..c5410653c
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.html
@@ -0,0 +1,21 @@
+
+ {{capability.description}}
+
+
+
+
+
+ Used by following applications
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.scss
new file mode 100644
index 000000000..2b17e917c
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.scss
@@ -0,0 +1,44 @@
+@import 'common';
+
+:host {
+ display: grid;
+ grid-template-columns: auto auto;
+ grid-column-gap: 1em;
+ grid-row-gap: .5em;
+ grid-template-rows: auto;
+
+ > section.description {
+ grid-column: 1/3;
+ font-style: italic;
+ }
+
+ > section {
+ border: 1px solid accentColor(.25);
+ border-radius: 5px;
+ padding: 1em;
+ background-color: #FFFFFF;
+ font-size: 12px;
+
+ > h2 {
+ font-size: 1.1em;
+ margin-top: 0;
+ margin-bottom: 1em;
+ }
+
+ > ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ > li > a.self {
+ font-style: italic;
+ }
+ }
+ }
+
+ &:not(.has-properties) {
+ > section.used-by {
+ grid-column: 1/3;
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.ts
new file mode 100644
index 000000000..e36dba748
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/capability-accordion-panel/capability-accordion-panel.component.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ChangeDetectionStrategy, Component, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { Application, Capability, ManifestRegistryService } from '@scion/workbench-application.core';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-capability-accordion-panel',
+ templateUrl: './capability-accordion-panel.component.html',
+ styleUrls: ['./capability-accordion-panel.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CapabilityAccordionPanelComponent implements OnChanges {
+
+ public consumers$: Observable;
+
+ @Input()
+ public capability: Capability;
+
+ @HostBinding('class.has-properties')
+ public hasProperties: boolean;
+
+ constructor(private _manifestRegistryService: ManifestRegistryService) {
+ }
+
+ public ngOnChanges(changes: SimpleChanges): void {
+ this.hasProperties = Object.keys(this.capability.properties || {}).length > 0;
+ this.consumers$ = this._manifestRegistryService.capabilityConsumers$(this.capability.metadata.id)
+ .pipe(map(consumers => [...consumers].sort((c1, c2) => c1.name.localeCompare(c2.name))));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/dev-tools-routing.module.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/dev-tools-routing.module.ts
new file mode 100644
index 000000000..022ea41f8
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/dev-tools-routing.module.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { ApplicationListComponent } from './application-list/application-list.component';
+import { ApplicationViewComponent } from './application-view/application-view.component';
+import { OutletCapabilityExecPopupComponent } from './outlet-capability-exec-popup/outlet-capability-exec-popup.component';
+
+const routes: Routes = [
+ {path: 'application-list', component: ApplicationListComponent},
+ {path: 'application/:symbolicName', component: ApplicationViewComponent},
+ {path: 'view-capability/:id', component: OutletCapabilityExecPopupComponent},
+ {path: 'popup-capability/:id', component: OutletCapabilityExecPopupComponent},
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class DevToolsRoutingModule {
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/dev-tools.module.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/dev-tools.module.ts
new file mode 100644
index 000000000..20181d142
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/dev-tools.module.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { SciViewportModule } from '@scion/viewport';
+import { SciAccordionModule, SciFilterFieldModule, SciListModule, SciParamsEnterModule, SciPopupShellModule, SciPropertyModule, SciSashModule } from '@scion/e2e/common';
+import { DevToolsRoutingModule } from './dev-tools-routing.module';
+import { ReactiveFormsModule } from '@angular/forms';
+import { WorkbenchApplicationModule } from '@scion/workbench-application.angular';
+import { ApplicationListComponent } from './application-list/application-list.component';
+import { ApplicationListItemComponent } from './application-list-item/application-list-item.component';
+import { ApplicationViewComponent } from './application-view/application-view.component';
+import { CapabilityAccordionItemComponent } from './capability-accordion-item/capability-accordion-item.component';
+import { QualifierChipListComponent } from './qualifier-chip-list/qualifier-chip-list.component';
+import { IntentAccordionItemComponent } from './intent-accordion-item/intent-accordion-item.component';
+import { IntentAccordionPanelComponent } from './intent-accordion-panel/intent-accordion-panel.component';
+import { CapabilityAccordionPanelComponent } from './capability-accordion-panel/capability-accordion-panel.component';
+import { OutletCapabilityExecPopupComponent } from './outlet-capability-exec-popup/outlet-capability-exec-popup.component';
+
+@NgModule({
+ declarations: [
+ ApplicationListComponent,
+ ApplicationListItemComponent,
+ ApplicationViewComponent,
+ CapabilityAccordionItemComponent,
+ CapabilityAccordionPanelComponent,
+ IntentAccordionItemComponent,
+ IntentAccordionPanelComponent,
+ QualifierChipListComponent,
+ OutletCapabilityExecPopupComponent,
+ ],
+ imports: [
+ CommonModule,
+ SciViewportModule,
+ ReactiveFormsModule,
+ WorkbenchApplicationModule.forChild(),
+ DevToolsRoutingModule,
+ SciListModule,
+ SciAccordionModule,
+ SciSashModule,
+ SciFilterFieldModule,
+ SciPopupShellModule,
+ SciParamsEnterModule,
+ SciPropertyModule,
+ ],
+ exports: [],
+ providers: []
+})
+export class DevToolsModule {
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.html
new file mode 100644
index 000000000..e3f6bf961
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.html
@@ -0,0 +1,5 @@
+
+
+ IMPLICIT
+ NOT HANDLED
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.scss
new file mode 100644
index 000000000..727e0abda
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.scss
@@ -0,0 +1,26 @@
+@import 'common';
+
+:host {
+ display: grid;
+ justify-content: space-between;
+ @include grid-container-align-items(center);
+ grid-template-columns: auto auto;
+ grid-template-rows: auto;
+ grid-column-gap: 1em;
+
+ > div.hints {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+
+ > span.implicit {
+ font-weight: bold;
+ @include chip(primaryColor(), null, primaryColor(), dotted);
+ }
+
+ > span.not-handled {
+ font-weight: bold;
+ @include chip(rgba(255, 0, 0, .75), rgba(255, 0, 0, .1), rgba(255, 0, 0, .75));
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.ts
new file mode 100644
index 000000000..e1c4bb23f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-item/intent-accordion-item.component.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { Intent, ManifestRegistryService } from '@scion/workbench-application.core';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-intent-accordion-item',
+ templateUrl: './intent-accordion-item.component.html',
+ styleUrls: ['./intent-accordion-item.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class IntentAccordionItemComponent implements OnChanges {
+
+ public anyQualifier: boolean;
+ public unhandled$: Observable;
+
+ @Input()
+ public intent: Intent;
+
+ constructor(private _manifestRegistryService: ManifestRegistryService) {
+ }
+
+ public ngOnChanges(changes: SimpleChanges): void {
+ this.anyQualifier = Object.keys(this.intent.qualifier || {}).includes('*');
+
+ this.unhandled$ = this._manifestRegistryService.capabilityProviders$(this.intent.metadata.id)
+ .pipe(map(providers => providers.length === 0));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.html
new file mode 100644
index 000000000..6590aa9fd
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.html
@@ -0,0 +1,12 @@
+
+ Handled by following applications
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.scss
new file mode 100644
index 000000000..cfca1cc74
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.scss
@@ -0,0 +1,30 @@
+@import 'common';
+
+:host {
+ display: flex;
+
+ > section {
+ border: 1px solid accentColor(.25);
+ border-radius: 5px;
+ padding: 1em;
+ background-color: #FFFFFF;
+ font-size: 12px;
+ flex: auto;
+
+ > h2 {
+ font-size: 1em;
+ margin-top: 0;
+ margin-bottom: 1em;
+ }
+
+ > ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ > li > a.self {
+ font-style: italic;
+ }
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.ts
new file mode 100644
index 000000000..4e683ec1a
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/intent-accordion-panel/intent-accordion-panel.component.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { Application, Intent, ManifestRegistryService } from '@scion/workbench-application.core';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-intent-accordion-panel',
+ templateUrl: './intent-accordion-panel.component.html',
+ styleUrls: ['./intent-accordion-panel.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class IntentAccordionPanelComponent implements OnChanges {
+
+ public anyQualifier: boolean;
+ public providers$: Observable;
+
+ @Input()
+ public intent: Intent;
+
+ constructor(private _manifestRegistryService: ManifestRegistryService) {
+ }
+
+ public ngOnChanges(changes: SimpleChanges): void {
+ this.anyQualifier = Object.keys(this.intent.qualifier || {}).includes('*');
+ this.providers$ = this._manifestRegistryService.capabilityProviders$(this.intent.metadata.id)
+ .pipe(map(providers => [...providers].sort((p1, p2) => p1.name.localeCompare(p2.name))));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.html
new file mode 100644
index 000000000..7d267eb5e
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.html
@@ -0,0 +1,13 @@
+
+ Execute {{capability?.type}} capability
+
+
+
+
+
+
+
+
+
+ Execute
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.scss
new file mode 100644
index 000000000..786446fba
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.scss
@@ -0,0 +1,21 @@
+:host {
+ display: flex;
+ flex-direction: column;
+
+ > sci-popup-shell {
+ flex: auto;
+
+ main {
+ display: flex;
+ flex-direction: column;
+
+ > sci-params-enter {
+ flex: none;
+
+ &:not(:last-child) {
+ margin-bottom: 1em;
+ }
+ }
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.ts
new file mode 100644
index 000000000..1611ee8cc
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/outlet-capability-exec-popup/outlet-capability-exec-popup.component.ts
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component, OnDestroy } from '@angular/core';
+import { provideWorkbenchPopup, WorkbenchPopup, WorkbenchRouter } from '@scion/workbench-application.angular';
+import { ActivatedRoute, Params } from '@angular/router';
+import { Capability, ManifestRegistryService, NotificationService, PlatformCapabilityTypes, PopupService, Qualifier } from '@scion/workbench-application.core';
+import { Subject } from 'rxjs';
+import { distinctUntilChanged, filter, map, switchMap, takeUntil, tap } from 'rxjs/operators';
+import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { FocusTrapFactory } from '@angular/cdk/a11y';
+import { PARAM_NAME, PARAM_VALUE, SciParamsEnterComponent } from '@scion/e2e/common';
+
+const QUALIFIER = 'qualifier';
+const QUERY_PARAMS = 'queryParams';
+const MATRIX_PARAMS = 'matrixParams';
+
+@Component({
+ selector: 'app-outlet-capability-exec-popup',
+ templateUrl: './outlet-capability-exec-popup.component.html',
+ styleUrls: ['./outlet-capability-exec-popup.component.scss'],
+ providers: [
+ provideWorkbenchPopup(OutletCapabilityExecPopupComponent)
+ ]
+})
+export class OutletCapabilityExecPopupComponent implements OnDestroy {
+
+ public readonly QUALIFIER = QUALIFIER;
+ public readonly QUERY_PARAMS = QUERY_PARAMS;
+ public readonly MATRIX_PARAMS = MATRIX_PARAMS;
+
+ public capability: Capability;
+ public form: FormGroup;
+ public valid: boolean;
+
+ private _destroy$ = new Subject();
+
+ constructor(route: ActivatedRoute,
+ manifestRegistryService: ManifestRegistryService,
+ notificationService: NotificationService,
+ formBuilder: FormBuilder,
+ private _popup: WorkbenchPopup,
+ private _focusTrapFactory: FocusTrapFactory,
+ private _workbenchRouter: WorkbenchRouter,
+ private _popupService: PopupService) {
+ this.form = formBuilder.group({
+ [QUALIFIER]: formBuilder.array([]),
+ [QUERY_PARAMS]: formBuilder.array([]),
+ [MATRIX_PARAMS]: formBuilder.array([]),
+ });
+
+ route.params
+ .pipe(
+ map(params => params['id']),
+ distinctUntilChanged(),
+ switchMap(capabilityId => manifestRegistryService.capability$(capabilityId)),
+ tap(manifest => !manifest && notificationService.notify({
+ severity: 'error',
+ title: 'Capability not found',
+ text: `No capability found with given id '${route.snapshot.params['id']}'`
+ })),
+ filter(Boolean),
+ takeUntil(this._destroy$),
+ )
+ .subscribe(capability => {
+ this.capability = capability;
+ this.createQualifierFormControls(capability.qualifier, formBuilder);
+ this.requestFocus();
+ });
+ }
+
+ private createQualifierFormControls(qualifier: Qualifier, formBuilder: FormBuilder): void {
+ const formArray = formBuilder.array([]);
+ Object.entries(qualifier).forEach(([key, value]) => {
+ const readonly = (value !== '*');
+ formArray.push(formBuilder.group({
+ [PARAM_NAME]: formBuilder.control({value: key, disabled: true}),
+ [PARAM_VALUE]: formBuilder.control({value: (readonly ? value : ''), disabled: readonly}, Validators.required),
+ }));
+ });
+ this.form.setControl(QUALIFIER, formArray);
+ }
+
+ private requestFocus(): void {
+ const focusTrap = this._focusTrapFactory.create(document.body);
+ focusTrap.focusInitialElementWhenReady().finally(() => focusTrap.destroy());
+ }
+
+ public onExecute(): void {
+ const qualifier: Qualifier = SciParamsEnterComponent.toParams(this.form.get(QUALIFIER) as FormArray);
+ const queryParams: Params = SciParamsEnterComponent.toParams(this.form.get(QUERY_PARAMS) as FormArray);
+ const matrixParams: Params = SciParamsEnterComponent.toParams(this.form.get(MATRIX_PARAMS) as FormArray);
+
+ switch (this.capability.type) {
+ case PlatformCapabilityTypes.View: {
+ this._workbenchRouter.navigate(qualifier, {
+ target: 'blank',
+ matrixParams,
+ queryParams
+ });
+ break;
+ }
+ case PlatformCapabilityTypes.Popup: {
+ this._popupService.open({
+ position: 'east',
+ anchor: event.target as Element,
+ queryParams: queryParams,
+ matrixParams: matrixParams
+ }, qualifier);
+ break;
+ }
+ default: {
+ throw Error(`[CapabilityNotHandledError] Capability type not handled [type=${this.capability.type}]`);
+ }
+ }
+
+ this._popup.close();
+ }
+
+ public ngOnDestroy(): void {
+ this._destroy$.next();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.html
new file mode 100644
index 000000000..965937a24
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.html
@@ -0,0 +1,9 @@
+
+ -
+ {{type}}
+
+ -
+ {{entry.key}}
+ {{entry.value}}
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.scss
new file mode 100644
index 000000000..d0fadf1f3
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.scss
@@ -0,0 +1,54 @@
+@import 'common';
+
+:host {
+
+ > ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+
+ > li {
+ @include chip(accentColor(.25), null, accentColor(1));
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: .25em 1em;
+
+ > span.key {
+ color: accentColor(.75)
+ }
+
+ > span.value {
+ font-size: larger;
+ font-weight: bold;
+ color: accentColor(1)
+ }
+
+ &::before {
+ display: block;
+ content: '«qualifier»';
+ font-size: smaller;
+ margin-bottom: .75em;
+ color: accentColor(.5);
+ }
+ }
+
+ > li.type {
+ @include chip(accentColor(.25), accentColor(.1), accentColor(1));
+ min-width: 75px;
+
+ > span.type {
+ font-size: 1.5em;
+ font-variant: small-caps;
+ font-weight: bold;
+ color: accentColor(1)
+ }
+
+ &::before {
+ content: '«type»';
+ }
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.ts
new file mode 100644
index 000000000..056ef7440
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/app/dev-tools/qualifier-chip-list/qualifier-chip-list.component.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { Qualifier } from '@scion/workbench-application.core';
+import { KeyValue } from '@angular/common';
+
+@Component({
+ selector: 'app-qualifier-chip-list',
+ templateUrl: './qualifier-chip-list.component.html',
+ styleUrls: ['./qualifier-chip-list.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class QualifierChipListComponent implements OnChanges {
+
+ private _qualifierKeys: string[];
+
+ @Input()
+ public qualifier: Qualifier;
+
+ @Input()
+ public type: string;
+
+ public ngOnChanges(changes: SimpleChanges): void {
+ this._qualifierKeys = Object.keys(this.qualifier || {});
+ }
+
+ /**
+ * Compares qualifier entries by their position in the object.
+ */
+ public qualifierKeyCompareFn = (a: KeyValue, b: KeyValue): number => {
+ return this._qualifierKeys.indexOf(a.key) - this._qualifierKeys.indexOf(b.key);
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/assets/.gitkeep b/projects/e2e/workbench-application-platform/dev-tools-app/src/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/assets/manifest.json b/projects/e2e/workbench-application-platform/dev-tools-app/src/assets/manifest.json
new file mode 100644
index 000000000..715973ec6
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/assets/manifest.json
@@ -0,0 +1,97 @@
+{
+ "name": "SCION Workbench Application Platform DevTools",
+ "baseUrl": "#",
+ "capabilities": [
+ {
+ "type": "activity",
+ "qualifier": {
+ "entity": "dev-tools"
+ },
+ "description": "Lists all registered applications and allows to show their manifests.",
+ "properties": {
+ "title": "DevTools",
+ "path": "dev-tools/application-list",
+ "itemText": "apps",
+ "itemCssClass": "material-icons",
+ "position": 9999999999
+ }
+ },
+ {
+ "type": "view",
+ "qualifier": {
+ "entity": "application-list"
+ },
+ "private": false,
+ "description": "Lists all registered applications and allows to show their manifests.",
+ "properties": {
+ "path": "dev-tools/application-list",
+ "title": "Application manifests"
+ }
+ },
+ {
+ "type": "view",
+ "qualifier": {
+ "entity": "application",
+ "symbolicName": "*"
+ },
+ "private": false,
+ "description": "Shows the application manifest with all its capabilities, intents and dependent applications.",
+ "properties": {
+ "path": "dev-tools/application/:symbolicName",
+ "title": "Application Manifest"
+ }
+ },
+ {
+ "type": "popup",
+ "qualifier": {
+ "entity": "capability",
+ "action": "execute",
+ "type": "view",
+ "capabilityId": "*"
+ },
+ "private": false,
+ "description": "Allows to execute a capability of the type 'view'.",
+ "properties": {
+ "path": "dev-tools/view-capability/:capabilityId",
+ "width": "400px",
+ "height": "500px"
+ }
+ },
+ {
+ "type": "popup",
+ "qualifier": {
+ "entity": "capability",
+ "action": "execute",
+ "type": "popup",
+ "capabilityId": "*"
+ },
+ "private": false,
+ "description": "Allows to execute a capability of the type 'popup'.",
+ "properties": {
+ "path": "dev-tools/popup-capability/:capabilityId",
+ "width": "400px",
+ "height": "500px"
+ }
+ }
+ ],
+ "intents": [
+ {
+ "type": "manifest-registry"
+ },
+ {
+ "type": "notification"
+ },
+ {
+ "type": "view",
+ "qualifier": {
+ "*": "*"
+ }
+ },
+ {
+ "type": "popup",
+ "qualifier": {
+ "*": "*"
+ }
+ }
+ ]
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/environments/environment.now.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/environments/environment.now.ts
new file mode 100644
index 000000000..dc39aca58
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/environments/environment.now.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+export const environment = {
+ production: true
+};
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/environments/environment.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/environments/environment.ts
new file mode 100644
index 000000000..2a785475f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/environments/environment.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --configuration=now` replaces `environment.ts` with `environment.now.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/index.html b/projects/e2e/workbench-application-platform/dev-tools-app/src/index.html
new file mode 100644
index 000000000..952fb2984
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ SCION Workbench Application Platform DevTools
+
+
+
+
+
+
+
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/main.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/main.ts
new file mode 100644
index 000000000..c7b673cf4
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/now.json b/projects/e2e/workbench-application-platform/dev-tools-app/src/now.json
new file mode 100644
index 000000000..a7c989237
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/now.json
@@ -0,0 +1,5 @@
+{
+ "version": 2,
+ "name": "scion-workbench-application-platform-devtools",
+ "alias": "scion-workbench-application-platform-devtools"
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/polyfills.ts b/projects/e2e/workbench-application-platform/dev-tools-app/src/polyfills.ts
new file mode 100644
index 000000000..f56cc3de6
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/polyfills.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/weak-map';
+import 'core-js/es6/set';
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+/**
+ * If the application will be indexed by Google Search, the following is required.
+ * Googlebot uses a renderer based on Chrome 41.
+ * https://developers.google.com/search/docs/guides/rendering
+ **/
+// import 'core-js/es6/array';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect';
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ */
+
+ // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+
+ /*
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ */
+// (window as any).__Zone_enable_cross_context_check = true;
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/src/styles.scss b/projects/e2e/workbench-application-platform/dev-tools-app/src/styles.scss
new file mode 100644
index 000000000..1506f1627
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/src/styles.scss
@@ -0,0 +1,3 @@
+@import 'common';
+
+@include app-theme();
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/tsconfig.app.json b/projects/e2e/workbench-application-platform/dev-tools-app/tsconfig.app.json
new file mode 100644
index 000000000..2209158a9
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/tsconfig.app.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../../out-tsc/dev-tools-app",
+ "types": []
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/dev-tools-app/tslint.json b/projects/e2e/workbench-application-platform/dev-tools-app/tslint.json
new file mode 100644
index 000000000..e3e7b64d8
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/dev-tools-app/tslint.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../../../tslint.json",
+ "rules": {
+ "directive-selector": [
+ true,
+ "attribute",
+ "app",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "app",
+ "kebab-case"
+ ]
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/browserslist b/projects/e2e/workbench-application-platform/host-app/browserslist
new file mode 100644
index 000000000..37371cb04
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/browserslist
@@ -0,0 +1,11 @@
+# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+#
+# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
+
+> 0.5%
+last 2 versions
+Firefox ESR
+not dead
+not IE 9-11
\ No newline at end of file
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/app.component.html b/projects/e2e/workbench-application-platform/host-app/src/app/app.component.html
new file mode 100644
index 000000000..b73617382
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/app.component.html
@@ -0,0 +1 @@
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/app.component.ts b/projects/e2e/workbench-application-platform/host-app/src/app/app.component.ts
new file mode 100644
index 000000000..169b1f23e
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/app.component.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html'
+})
+export class AppComponent {
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/app.module.ts b/projects/e2e/workbench-application-platform/host-app/src/app/app.module.ts
new file mode 100644
index 000000000..222c6f1f4
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/app.module.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+
+import { AppComponent } from './app.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { WorkbenchModule } from '@scion/workbench';
+import { RouterModule } from '@angular/router';
+import { WorkbenchApplicationPlatformModule } from '@scion/workbench-application-platform';
+import { CustomExtensionModule } from './custom-extension/custom-extension.module';
+import { environment } from '../environments/environment';
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ],
+ imports: [
+ WorkbenchModule.forRoot(),
+ WorkbenchApplicationPlatformModule.forRoot({
+ applicationConfig: [
+ {
+ symbolicName: 'contact-app',
+ manifestUrl: environment.contact_app_manifest_url,
+ },
+ {
+ symbolicName: 'communication-app',
+ manifestUrl: environment.communication_app_manifest_url,
+ },
+ {
+ symbolicName: 'devtools-app',
+ manifestUrl: environment.devtools_app_manifest_url,
+ scopeCheckDisabled: true,
+ },
+ {
+ symbolicName: 'testing-app',
+ exclude: !environment.testing_app_manifest_url,
+ manifestUrl: environment.testing_app_manifest_url,
+ },
+ {
+ symbolicName: 'angular-io-app',
+ manifestUrl: '/assets/angular-io-manifest.json',
+ },
+ ],
+ }),
+ RouterModule.forRoot([], {
+ useHash: true,
+ initialNavigation: 'disabled'
+ }),
+ BrowserAnimationsModule,
+ BrowserModule,
+ CustomExtensionModule,
+ ],
+ bootstrap: [
+ AppComponent
+ ]
+})
+export class AppModule {
+}
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-extension.module.ts b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-extension.module.ts
new file mode 100644
index 000000000..270a91107
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-extension.module.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ListMessageboxComponent } from './list-messagebox/list-messagebox.component';
+import { ACTIVITY_ACTION_PROVIDER, INTENT_HANDLER, MessageBoxIntentHandler, NotificationIntentHandler } from '@scion/workbench-application-platform';
+import { ListNotificationComponent } from './list-notification/list-notification.component';
+import { CustomNotifyActivityActionComponent } from './custom-notify-activity-action/custom-notify-activity-action.component';
+import { CustomNotifyActivityActionProvider } from './custom-notify-activity-action/custom-notify-activity-action-provider.service';
+
+@NgModule({
+ declarations: [
+ ListMessageboxComponent,
+ ListNotificationComponent,
+ CustomNotifyActivityActionComponent,
+ ],
+ imports: [
+ CommonModule,
+ ],
+ entryComponents: [
+ ListMessageboxComponent,
+ ListNotificationComponent,
+ CustomNotifyActivityActionComponent,
+ ],
+ providers: [
+ {
+ provide: INTENT_HANDLER,
+ useFactory: provideListMessageBoxIntentHandler,
+ multi: true,
+ },
+ {
+ provide: INTENT_HANDLER,
+ useFactory: provideListNotificationIntentHandler,
+ multi: true,
+ },
+ {
+ provide: ACTIVITY_ACTION_PROVIDER,
+ useClass: CustomNotifyActivityActionProvider,
+ multi: true
+ },
+ ],
+})
+export class CustomExtensionModule {
+}
+
+export function provideListMessageBoxIntentHandler(): MessageBoxIntentHandler {
+ return new MessageBoxIntentHandler({'type': 'list'}, 'Displays a messagebox with list content to the user.', ListMessageboxComponent);
+}
+
+export function provideListNotificationIntentHandler(): NotificationIntentHandler {
+ return new NotificationIntentHandler({'type': 'list'}, 'Shows a notification with list content to the user.', ListNotificationComponent);
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action-provider.service.ts b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action-provider.service.ts
new file mode 100644
index 000000000..8800c47e9
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action-provider.service.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Injectable } from '@angular/core';
+import { ActivityActionProvider } from '@scion/workbench-application-platform';
+import { CustomNotifyActivityActionComponent } from './custom-notify-activity-action.component';
+import { CustomActivityActionTypes } from '@scion/e2e/common';
+
+@Injectable()
+export class CustomNotifyActivityActionProvider implements ActivityActionProvider {
+ public type = CustomActivityActionTypes.CustomNotify;
+ public component = CustomNotifyActivityActionComponent;
+}
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.html b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.html
new file mode 100644
index 000000000..aeb7d75de
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.html
@@ -0,0 +1,7 @@
+
+ notifications_none
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.scss b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.scss
new file mode 100644
index 000000000..1400644bc
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.scss
@@ -0,0 +1,12 @@
+:host {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: .5em;
+
+ > a {
+ text-decoration: none;
+ color: rgb(239, 239, 239);
+ outline: none;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.ts b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.ts
new file mode 100644
index 000000000..b4abc5236
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/custom-notify-activity-action/custom-notify-activity-action.component.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component, Inject } from '@angular/core';
+import { NotificationService } from '@scion/workbench';
+import { ACTIVITY_ACTION } from '@scion/workbench-application-platform';
+import { CustomNotifyActivityAction } from '@scion/e2e/common';
+
+@Component({
+ selector: 'app-custom-notify-activity-action',
+ templateUrl: './custom-notify-activity-action.component.html',
+ styleUrls: ['./custom-notify-activity-action.component.scss']
+})
+export class CustomNotifyActivityActionComponent {
+
+ constructor(@Inject(ACTIVITY_ACTION) public action: CustomNotifyActivityAction,
+ private _notificationService: NotificationService) {
+ }
+
+ public onClick(event: Event): void {
+ event.preventDefault();
+ this._notificationService.notify({
+ content: this.action.properties.text,
+ cssClass: this.action.properties.cssClass,
+ });
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.html b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.html
new file mode 100644
index 000000000..405ac2b7f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.html
@@ -0,0 +1,4 @@
+{{messageBox.input?.text}}
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.scss b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.scss
new file mode 100644
index 000000000..965a822dc
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.scss
@@ -0,0 +1,5 @@
+:host {
+ > ul {
+ padding: 0 0 0 1.5em;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.ts b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.ts
new file mode 100644
index 000000000..4c7fbc571
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-messagebox/list-messagebox.component.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component } from '@angular/core';
+import { MessageBox } from '@scion/workbench';
+
+@Component({
+ selector: 'app-list-messagebox',
+ templateUrl: './list-messagebox.component.html',
+ styleUrls: ['./list-messagebox.component.scss'],
+})
+export class ListMessageboxComponent {
+
+ constructor(public messageBox: MessageBox) {
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.html b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.html
new file mode 100644
index 000000000..c72874272
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.html
@@ -0,0 +1,4 @@
+{{notification.input?.text}}
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.scss b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.scss
new file mode 100644
index 000000000..965a822dc
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.scss
@@ -0,0 +1,5 @@
+:host {
+ > ul {
+ padding: 0 0 0 1.5em;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.ts b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.ts
new file mode 100644
index 000000000..bb1669e22
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/app/custom-extension/list-notification/list-notification.component.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { Component } from '@angular/core';
+import { Notification } from '@scion/workbench';
+
+@Component({
+ selector: 'app-list-notification',
+ templateUrl: './list-notification.component.html',
+ styleUrls: ['./list-notification.component.scss']
+})
+export class ListNotificationComponent {
+
+ constructor(public notification: Notification) {
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/assets/.gitkeep b/projects/e2e/workbench-application-platform/host-app/src/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/projects/e2e/workbench-application-platform/host-app/src/assets/angular-io-manifest.json b/projects/e2e/workbench-application-platform/host-app/src/assets/angular-io-manifest.json
new file mode 100644
index 000000000..2734593ee
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/assets/angular-io-manifest.json
@@ -0,0 +1,30 @@
+{
+ "name": "Angular IO",
+ "baseUrl": "https://angular.io/",
+ "capabilities": [
+ {
+ "type": "view",
+ "qualifier": {
+ "resource": "features"
+ },
+ "description": "Shows features & benefits of the Angular project",
+ "properties": {
+ "title": "Angular",
+ "heading": "Features & Benefits",
+ "path": "/features"
+ }
+ },
+ {
+ "type": "view",
+ "qualifier": {
+ "resource": "docs"
+ },
+ "description": "Shows the documentation of the Angular project",
+ "properties": {
+ "title": "Angular",
+ "heading": "Documentation",
+ "path": "/docs"
+ }
+ }
+ ]
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.eot b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.eot
new file mode 100644
index 000000000..460bbadc1
Binary files /dev/null and b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.eot differ
diff --git a/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.svg b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.svg
new file mode 100644
index 000000000..3a2f8477f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.svg
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.ttf b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.ttf
new file mode 100644
index 000000000..042af077e
Binary files /dev/null and b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.ttf differ
diff --git a/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.woff b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.woff
new file mode 100644
index 000000000..8e98ad140
Binary files /dev/null and b/projects/e2e/workbench-application-platform/host-app/src/assets/fonts/wb-icon.woff differ
diff --git a/projects/e2e/workbench-application-platform/host-app/src/environments/environment.now.ts b/projects/e2e/workbench-application-platform/host-app/src/environments/environment.now.ts
new file mode 100644
index 000000000..17a8085bc
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/environments/environment.now.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+export const environment = {
+ production: true,
+ contact_app_manifest_url: 'https://scion-workbench-application-platform-contact.now.sh/assets/manifest.json',
+ communication_app_manifest_url: 'https://scion-workbench-application-platform-communication.now.sh/assets/manifest.json',
+ devtools_app_manifest_url: 'https://scion-workbench-application-platform-devtools.now.sh/assets/manifest.json',
+ testing_app_manifest_url: ''
+};
diff --git a/projects/e2e/workbench-application-platform/host-app/src/environments/environment.ts b/projects/e2e/workbench-application-platform/host-app/src/environments/environment.ts
new file mode 100644
index 000000000..84e48f7cf
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/environments/environment.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --configuration=now` replaces `environment.ts` with `environment.now.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false,
+ contact_app_manifest_url: 'http://localhost:5001/assets/manifest.json',
+ communication_app_manifest_url: 'http://localhost:5002/assets/manifest.json',
+ devtools_app_manifest_url: 'http://localhost:5003/assets/manifest.json',
+ testing_app_manifest_url: 'http://localhost:5004/assets/manifest.json',
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/projects/e2e/workbench-application-platform/host-app/src/favicon.ico b/projects/e2e/workbench-application-platform/host-app/src/favicon.ico
new file mode 100644
index 000000000..30ac1ce19
Binary files /dev/null and b/projects/e2e/workbench-application-platform/host-app/src/favicon.ico differ
diff --git a/projects/e2e/workbench-application-platform/host-app/src/index.html b/projects/e2e/workbench-application-platform/host-app/src/index.html
new file mode 100644
index 000000000..0f3dc69af
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ Workbench Application Platform
+
+
+
+
+
+
+
+
diff --git a/projects/e2e/workbench-application-platform/host-app/src/main.ts b/projects/e2e/workbench-application-platform/host-app/src/main.ts
new file mode 100644
index 000000000..c7b673cf4
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/projects/e2e/workbench-application-platform/host-app/src/now.json b/projects/e2e/workbench-application-platform/host-app/src/now.json
new file mode 100644
index 000000000..2f95df25c
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/now.json
@@ -0,0 +1,5 @@
+{
+ "version": 2,
+ "name": "scion-workbench-application-platform",
+ "alias": "scion-workbench-application-platform"
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/src/polyfills.ts b/projects/e2e/workbench-application-platform/host-app/src/polyfills.ts
new file mode 100644
index 000000000..1d7b9ab94
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/polyfills.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/weak-map';
+import 'core-js/es6/set';
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ **/
+import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+/**
+ * If the application will be indexed by Google Search, the following is required.
+ * Googlebot uses a renderer based on Chrome 41.
+ * https://developers.google.com/search/docs/guides/rendering
+ **/
+// import 'core-js/es6/array';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect';
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ */
+
+ // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+
+ /*
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ */
+// (window as any).__Zone_enable_cross_context_check = true;
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/projects/e2e/workbench-application-platform/host-app/src/styles.scss b/projects/e2e/workbench-application-platform/host-app/src/styles.scss
new file mode 100644
index 000000000..ff72f92b8
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/src/styles.scss
@@ -0,0 +1,5 @@
+@import '../../../../scion/workbench/src/theme/theming';
+@import 'common';
+
+@include wb-theme();
+@include app-theme();
diff --git a/projects/e2e/workbench-application-platform/host-app/tsconfig.app.json b/projects/e2e/workbench-application-platform/host-app/tsconfig.app.json
new file mode 100644
index 000000000..f06ee9dc5
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/tsconfig.app.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../../out-tsc/host-app",
+ "types": []
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/host-app/tslint.json b/projects/e2e/workbench-application-platform/host-app/tslint.json
new file mode 100644
index 000000000..e3e7b64d8
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/host-app/tslint.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../../../tslint.json",
+ "rules": {
+ "directive-selector": [
+ true,
+ "attribute",
+ "app",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "app",
+ "kebab-case"
+ ]
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/protractor.conf.js b/projects/e2e/workbench-application-platform/test-runner/protractor.conf.js
new file mode 100644
index 000000000..a224f8a3f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/protractor.conf.js
@@ -0,0 +1,35 @@
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter } = require('jasmine-spec-reporter');
+
+const puppeteer = require('puppeteer');
+
+exports.config = {
+ allScriptsTimeout: 11000,
+ specs: [
+ './src/**/*.e2e-spec.ts'
+ ],
+ capabilities: {
+ 'browserName': 'chrome',
+ chromeOptions: {
+ args: ['--headless', '--window-size=1920,1080'],
+ binary: puppeteer.executablePath(),
+ },
+ },
+ SELENIUM_PROMISE_MANAGER: false,
+ directConnect: true,
+ baseUrl: 'http://localhost:5000/',
+ framework: 'jasmine',
+ jasmineNodeOpts: {
+ showColors: true,
+ defaultTimeoutInterval: 30000,
+ print: function() {}
+ },
+ onPrepare() {
+ require('ts-node').register({
+ project: require('path').join(__dirname, './tsconfig.e2e.json')
+ });
+ jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+ }
+};
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/activity.e2e-spec.ts b/projects/e2e/workbench-application-platform/test-runner/src/activity.e2e-spec.ts
new file mode 100644
index 000000000..4554ef31f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/activity.e2e-spec.ts
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { HostAppPO } from './page-object/host-app.po';
+import { browser, protractor } from 'protractor';
+import { expectActivityToExistButHidden, expectActivityToShow, expectPopupToNotExist, expectPopupToShow, expectViewToNotExist, expectViewToShow } from './util/testing.util';
+import { TestingActivityPO } from './page-object/testing-activity.po';
+import { Testcase4a3a8984ActivityPO } from './page-object/testcase-4a3a8984-activity-po';
+import { Testcase28f32b51ActivityPO } from './page-object/testcase-28f32b51-activity.po';
+import { Testcase5782ab19ActivityPO } from './page-object/testcase-5782ab19-activity.po';
+import { Testcase56657ad1ViewPO } from './page-object/testcase-56657ad1-view.po';
+import { TestcaseC8e40918ViewPO } from './page-object/testcase-c8e40918-view.po';
+import { TestcaseA686d615ViewPO } from './page-object/testcase-a686d615-view.po';
+import { TestingViewPO } from './page-object/testing-view.po';
+import { TestcaseCc977da9ViewPO } from './page-object/testcase-cc977da9-view.po';
+import { TestcaseB6a8fe23ViewPO } from './page-object/testcase-b6a8fe23-view.po';
+import { Testcase9c5319f7PopupPO } from './page-object/testcase-9c5319f7-popup.po';
+import { Testcase45dc693fPopupPO } from './page-object/testcase-45dc693f-popup.po';
+import { TestcaseF4286ac4PopupPO } from './page-object/testcase-f4286ac4-popup.po';
+import { Testcase159913adPopupPO } from './page-object/testcase-159913ad-popup.po';
+import { Testcase8a468258PopupPO } from './page-object/testcase-8a468258-popup.po';
+
+const E2E_TESTING_ACTIVITY_CONTEXT: string[] = ['e2e-testing-app', 'e2e-activity', 'e2e-testing-activity'];
+
+describe('Activity', () => {
+
+ const hostAppPO = new HostAppPO();
+
+ beforeEach(async () => {
+ await browser.get('/');
+ });
+
+ describe('Registration', () => {
+
+ it('should be registered as specified in the manifest [testcase: d11be592-activity]', async () => {
+ const activityItemPO = await hostAppPO.findActivityItem('e2e-activity-d11be592');
+
+ await expect(activityItemPO.getTitle()).toEqual('d29b10b862ae');
+ await expect(activityItemPO.getText()).toEqual('');
+ await expect(activityItemPO.getCssClasses()).toContain('e2e-activity-d11be592');
+ await expect(activityItemPO.getCssClasses()).toContain('fab');
+ await expect(activityItemPO.getCssClasses()).toContain('fa-android');
+
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-d11be592', componentSelector: 'app-testing-activity'});
+
+ const testingActivityPO = new TestingActivityPO(['e2e-testing-app', 'e2e-activity', 'e2e-activity-d11be592']);
+ const activityPanelPO = await hostAppPO.findActivityPanel('e2e-activity-d11be592');
+ await expect(testingActivityPO.getUrlParameters()).toEqual({manifestMatrixParam: 'manifestMatrixParamValue'});
+ await expect(testingActivityPO.getUrlQueryParameters()).toEqual({manifestQueryParam: 'manifestQueryParamValue'});
+ await expect(activityPanelPO.getTitle()).toEqual('d29b10b862ae');
+ await expect(activityPanelPO.getCssClasses()).toContain('e2e-activity-d11be592');
+ });
+ });
+
+ describe('Interaction', () => {
+
+ it('should allow to change activity properties', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+
+ const activityInteractionPO = await testingActivityPO.openActivityInteractionPanel();
+ const activityItemPO = await hostAppPO.findActivityItem('e2e-testing-activity');
+ const activityPanelPO = await hostAppPO.findActivityPanel('e2e-testing-activity');
+
+ await activityInteractionPO.enterTitle('e2e:title');
+ await expect(activityItemPO.getTitle()).toEqual('e2e:title');
+ await expect(activityPanelPO.getTitle()).toEqual('e2e:title');
+
+ await activityInteractionPO.enterItemText('e2e:item-text');
+ await expect(activityItemPO.getText()).toEqual('e2e:item-text');
+
+ await activityInteractionPO.enterItemCssClass('e2e:item-css-class');
+ await expect(activityItemPO.getCssClasses()).toContain('e2e:item-css-class');
+ await expect(activityPanelPO.getCssClasses()).not.toContain('e2e:item-css-class');
+
+ const sizeBefore = await activityPanelPO.getSize();
+ await activityInteractionPO.enterDeltaPx(100);
+ const sizeAfter = await activityPanelPO.getSize();
+ await expect(sizeAfter.width).toEqual(sizeBefore.width + 100);
+ });
+
+ it('should allow to close the activity', async () => {
+ const activityItemPO = await hostAppPO.findActivityItem('e2e-testing-activity');
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+
+ // close the activity
+ await activityItemPO.click();
+ await expectActivityToExistButHidden({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity'});
+ });
+
+ it('should attach previously detached activity component instance when activating it [testcase: 4a3a8984-activity]', async () => {
+ const activityItemPO = await hostAppPO.findActivityItem('e2e-activity-4a3a8984');
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-4a3a8984', componentSelector: 'app-activity-4a3a8984'});
+
+ const activityPO = new Testcase4a3a8984ActivityPO();
+ const componentInstanceUuid = await activityPO.getComponentInstanceUuid();
+
+ // close the activity
+ await activityItemPO.click();
+ await expectActivityToExistButHidden({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-4a3a8984'});
+
+ // open the activity
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-4a3a8984', componentSelector: 'app-activity-4a3a8984'});
+ await expect(activityPO.getComponentInstanceUuid()).toEqual(componentInstanceUuid);
+
+ // switch to other activity
+ const testingActivityItemPO = await hostAppPO.findActivityItem('e2e-testing-activity');
+ await testingActivityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+
+ // switch back to the activity
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-4a3a8984', componentSelector: 'app-activity-4a3a8984'});
+ await expect(activityPO.getComponentInstanceUuid()).toEqual(componentInstanceUuid);
+ });
+
+ it('should receive an activate event if activated, and receive a deactivate event if deactivated [testcase: 28f32b51-activity]', async () => {
+ const activityItemPO = await hostAppPO.findActivityItem('e2e-activity-28f32b51');
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-28f32b51', componentSelector: 'app-activity-28f32b51'});
+
+ const activityPO = new Testcase28f32b51ActivityPO();
+ await expect(activityPO.readActiveLog()).toEqual([true]);
+
+ // close the activity
+ await activityItemPO.click();
+ await expectActivityToExistButHidden({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-28f32b51'});
+
+ // open the activity
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-28f32b51', componentSelector: 'app-activity-28f32b51'});
+ await expect(activityPO.readActiveLog()).toEqual([true, false, true]);
+
+ // switch to other activity
+ const testingActivityItemPO = await hostAppPO.findActivityItem('e2e-testing-activity');
+ await testingActivityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+
+ // switch back to the activity
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-28f32b51', componentSelector: 'app-activity-28f32b51'});
+ await expect(activityPO.readActiveLog()).toEqual([true, false, true, false, true]);
+ });
+
+ it('should not show the iframe of inactive activities [testcase: 6d806bea-activity]', async () => {
+ const activityItemPO = await hostAppPO.findActivityItem('e2e-activity-6d806bea');
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-6d806bea', componentSelector: 'app-activity-6d806bea'});
+
+ // close the activity
+ await activityItemPO.click();
+ await expectActivityToExistButHidden({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-6d806bea'});
+
+ // open the activity
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-6d806bea', componentSelector: 'app-activity-6d806bea'});
+
+ // switch to other activity
+ const testingActivityItemPO = await hostAppPO.findActivityItem('e2e-testing-activity');
+ await testingActivityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+ await expectActivityToExistButHidden({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-6d806bea'});
+
+ // switch back to the activity
+ await activityItemPO.click();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-6d806bea', componentSelector: 'app-activity-6d806bea'});
+ await expectActivityToExistButHidden({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity'});
+ });
+ });
+
+ describe('Focus', () => {
+
+ it('should cycle the focus in the activity [testcase: 5782ab19-activity]', async () => {
+ await hostAppPO.clickActivityItem('e2e-activity-5782ab19');
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-5782ab19', componentSelector: 'app-activity-5782ab19'});
+
+ const activityPO = new Testcase5782ab19ActivityPO();
+ await expect(activityPO.isActiveElement('field-1')).toBeTruthy('(1)');
+
+ await activityPO.pressTab();
+ await expect(activityPO.isActiveElement('field-2')).toBeTruthy('(2)');
+
+ await activityPO.pressTab();
+ await expect(activityPO.isActiveElement('field-3')).toBeTruthy('(3)');
+
+ await activityPO.pressTab();
+ await expect(activityPO.isActiveElement('field-1')).toBeTruthy('(4)');
+
+ await activityPO.pressTab();
+ await expect(activityPO.isActiveElement('field-2')).toBeTruthy('(5)');
+
+ await activityPO.pressTab();
+ await expect(activityPO.isActiveElement('field-3')).toBeTruthy('(6)');
+
+ await activityPO.pressShiftTab();
+ await expect(activityPO.isActiveElement('field-2')).toBeTruthy('(7)');
+
+ await activityPO.pressShiftTab();
+ await expect(activityPO.isActiveElement('field-1')).toBeTruthy('(8)');
+
+ await activityPO.pressShiftTab();
+ await expect(activityPO.isActiveElement('field-3')).toBeTruthy('(9)');
+
+ await activityPO.pressShiftTab();
+ await expect(activityPO.isActiveElement('field-2')).toBeTruthy('(10)');
+
+ await activityPO.pressShiftTab();
+ await expect(activityPO.isActiveElement('field-1')).toBeTruthy('(11)');
+ });
+
+ it('should restore the focus upon activity activation [testcase: 5782ab19-activity]', async () => {
+ await hostAppPO.clickActivityItem('e2e-activity-5782ab19');
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-5782ab19', componentSelector: 'app-activity-5782ab19'});
+
+ const activityPO = new Testcase5782ab19ActivityPO();
+ await expect(activityPO.isActiveElement('field-1')).toBeTruthy('(1)');
+
+ await activityPO.pressTab();
+ await expect(activityPO.isActiveElement('field-2')).toBeTruthy('(2)');
+
+ await hostAppPO.clickActivityItem('e2e-testing-activity');
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+
+ await hostAppPO.clickActivityItem('e2e-activity-5782ab19');
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-5782ab19', componentSelector: 'app-activity-5782ab19'});
+ await expect(activityPO.isActiveElement('field-2')).toBeTruthy('(3)');
+ });
+
+ it('should autofocus the first element [testcase: 5782ab19-activity]', async () => {
+ await hostAppPO.clickActivityItem('e2e-activity-5782ab19');
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-activity-5782ab19', componentSelector: 'app-activity-5782ab19'});
+
+ const activityPO = new Testcase5782ab19ActivityPO();
+ await expect(activityPO.isActiveElement('field-1')).toBeTruthy('Expected first field to be the active element');
+ });
+ });
+
+ describe('Activity View Item', () => {
+
+ it('should allow navigation to private views of the same application (implicit intent) [testcase: 354aa6da-activity]', async () => {
+ await hostAppPO.clickActivityItem('e2e-activity-354aa6da');
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-354aa6da', componentSelector: 'app-view-354aa6da'});
+ });
+
+ it('should allow navigation to public views of the same application (implicit intent) [testcase: 85dde646-activity]', async () => {
+ await hostAppPO.clickActivityItem('e2e-activity-85dde646');
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-85dde646', componentSelector: 'app-view-85dde646'});
+ });
+ });
+
+ describe('Activity Action', () => {
+
+ it('should allow to add and remove activity actions [testcase: 354aa6da-activity]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+ const actionPanelPO = await testingActivityPO.openActivityActionsPanel();
+
+ // hide action
+ const action = await actionPanelPO.findCustomActivityAction();
+ await actionPanelPO.checkCustomActivityAction(false);
+ await expect(action.isPresent()).toBeFalsy('(1)');
+
+ // show action
+ await actionPanelPO.checkCustomActivityAction(true);
+ await expect(action.isPresent()).toBeTruthy('(2)');
+
+ // hide action
+ await actionPanelPO.checkCustomActivityAction(false);
+ await expect(action.isPresent()).toBeFalsy('(3)');
+ });
+
+ it('should allow to contribute custom activity actions', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+ await expectActivityToShow({symbolicAppName: 'testing-app', activityCssClass: 'e2e-testing-activity', componentSelector: 'app-testing-activity'});
+ const actionPanelPO = await testingActivityPO.openActivityActionsPanel();
+ await actionPanelPO.checkCustomActivityAction(true);
+
+
+ const customAction = await hostAppPO.findActivityAction('e2e-custom-activity-action');
+ await customAction.click();
+
+ await expect(await hostAppPO.findNotification('e2e-custom-activity-action')).toBeTruthy();
+ });
+
+ describe('ViewOpenActivityAction', () => {
+
+ it('should allow navigation to private views of the same application (implicit intent) [testcase: 354aa6da-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-354aa6da-view-activity-action');
+ await panelPO.enterTitle('testcase: 354aa6da-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '354aa6da-view',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-354aa6da-view-activity-action');
+ await action.click();
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-354aa6da', componentSelector: 'app-view-354aa6da'});
+ });
+
+ it('should allow navigation to public views of the same application (implicit intent) [testcase: 85dde646-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-85dde646-view-activity-action');
+ await panelPO.enterTitle('testcase: 85dde646-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '85dde646-view',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-85dde646-view-activity-action');
+ await action.click();
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-85dde646', componentSelector: 'app-view-85dde646'});
+ });
+
+ it('should allow navigation to public views of other applications [testcase: d7da51f14c25]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-d7da51f14c25-activity-action');
+ await panelPO.enterTitle('testcase: d7da51f14c25');
+ await panelPO.enterQualifier({
+ entity: 'communication',
+ presentation: 'list',
+ contactId: '5',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-d7da51f14c25-activity-action');
+ await action.click();
+ await expectViewToShow({symbolicAppName: 'communication-app', viewCssClass: 'e2e-communication-list', componentSelector: 'app-communication-view'});
+ });
+
+ it('should not allow navigation to public views of other applications if missing the intent [testcase: 0901198a704b]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-0901198a704b-activity-action');
+ await panelPO.enterTitle('testcase: 0901198a704b');
+ await panelPO.enterQualifier({
+ entity: 'communication',
+ presentation: 'list',
+ contactId: '999',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-0901198a704b-activity-action');
+ await action.click();
+
+ const notificationPO = await hostAppPO.findNotification('e2e-not-qualified');
+ await expect(notificationPO).not.toBeNull();
+ await expect(notificationPO.getSeverity()).toEqual('error');
+ await expectViewToNotExist({symbolicAppName: 'communication-app', viewCssClass: 'e2e-communication-list'});
+ });
+
+ it('should not allow navigation to private views of other application [testcase: 0427da7f4a49]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-0427da7f4a49-activity-action');
+ await panelPO.enterTitle('testcase: 0427da7f4a49');
+ await panelPO.enterQualifier({
+ entity: 'contact',
+ id: '5',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-0427da7f4a49-activity-action');
+ await action.click();
+
+ const notificationPO = await hostAppPO.findNotification('e2e-not-handled');
+ await expect(notificationPO).not.toBeNull();
+ await expect(notificationPO.getSeverity()).toEqual('error');
+ await expectViewToNotExist({symbolicAppName: 'contact-app', viewCssClass: 'e2e-contact'});
+ });
+
+ it('should allow to provide query and matrix parameters [testcase: 56657ad1-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-56657ad1-view-activity-action');
+ await panelPO.enterTitle('testcase: 56657ad1-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '56657ad1-view',
+ });
+ await panelPO.enterMatrixParams({
+ mp1: '41ecdefec0a3',
+ mp2: 'da67bd554b36',
+ });
+ await panelPO.enterQueryParams({
+ qp1: '6378a62700d3',
+ qp2: 'c5a37d3660ba',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-56657ad1-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-56657ad1', componentSelector: 'app-view-56657ad1'});
+
+ const viewPO = new Testcase56657ad1ViewPO();
+ const urlParams = await viewPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ mp1: '41ecdefec0a3',
+ mp2: 'da67bd554b36',
+ });
+
+ const urlQueryParams = await viewPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ qp1: '6378a62700d3',
+ qp2: 'c5a37d3660ba',
+ });
+ });
+
+ it('should allow to receive query and matrix parameters as specified in the manifest [testcase: c8e40918-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-c8e40918-view-activity-action');
+ await panelPO.enterTitle('testcase: c8e40918-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: 'c8e40918-view',
+ });
+
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-c8e40918-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-c8e40918', componentSelector: 'app-view-c8e40918'});
+
+ const viewPO = new TestcaseC8e40918ViewPO();
+ const urlParams = await viewPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ mp1: 'd52f5f88be27',
+ mp2: '01aa011fb2f4',
+ });
+
+ const urlQueryParams = await viewPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ qp1: 'f3227d5926c0',
+ qp2: '88a9cd0cb937',
+ });
+ });
+
+ it('should allow to merge query and matrix parameters provided from intent (overwrite) and manifest [testcase: a686d615-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-a686d615-view-activity-action');
+ await panelPO.enterTitle('testcase: a686d615-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: 'a686d615-view',
+ });
+ await panelPO.enterMatrixParams({
+ mp1: '557c7323a13c',
+ mp2: '67dbebc8dd7c',
+ });
+ await panelPO.enterQueryParams({
+ qp1: 'a3fa547519cf',
+ qp2: 'db03e5494054',
+ });
+
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-a686d615-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-a686d615', componentSelector: 'app-view-a686d615'});
+
+ const viewPO = new TestcaseA686d615ViewPO();
+ const urlParams = await viewPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ mp1: '557c7323a13c',
+ mp2: '67dbebc8dd7c',
+ mpx: 'f406422c77fc',
+ });
+
+ const urlQueryParams = await viewPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ qp1: 'a3fa547519cf',
+ qp2: 'db03e5494054',
+ qpx: '18c9d6c51b0c',
+ });
+ });
+
+ it('should allow to navigate to an opened view (activateIfPresent=true) [testcase: 4a4e6970-view]', async () => {
+ // Open testing view to open '4a4e6970-view'
+ const testingViewPO = new TestingViewPO();
+ await testingViewPO.navigateTo();
+ const viewNavigationPO = await testingViewPO.openViewNavigationPanel();
+ await viewNavigationPO.enterQualifier({
+ entity: 'testing',
+ testcase: '4a4e6970-view',
+ });
+ await viewNavigationPO.selectTarget('blank');
+ await viewNavigationPO.execute();
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-4a4e6970', componentSelector: 'app-view-4a4e6970'});
+
+ // Go back to the testing view
+ await hostAppPO.clickViewTab('e2e-testing-view');
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-testing-view', componentSelector: 'app-testing-view'});
+ await expect(hostAppPO.getViewTabCount()).toBe(2);
+
+ // Add activity action to activate the testing view
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-4a4e6970-view-activity-action');
+ await panelPO.enterTitle('testcase: 4a4e6970-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '4a4e6970-view',
+ });
+ await panelPO.checkActivateIfPresent(true);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-4a4e6970-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-4a4e6970', componentSelector: 'app-view-4a4e6970'});
+ await expect(hostAppPO.getViewTabCount()).toBe(2);
+ });
+
+
+ it('should allow to close an opened view (closeIfPresent=true) [testcase: 608aa47c-view]', async () => {
+ // Open testing view to open '4a4e6970-view'
+ const testingViewPO = new TestingViewPO();
+ await testingViewPO.navigateTo();
+ const viewNavigationPO = await testingViewPO.openViewNavigationPanel();
+ await viewNavigationPO.enterQualifier({
+ entity: 'testing',
+ testcase: '608aa47c-view',
+ });
+ await viewNavigationPO.selectTarget('blank');
+ await viewNavigationPO.execute();
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-608aa47c', componentSelector: 'app-view-608aa47c'});
+
+ // Add activity action to close the testing view
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-608aa47c-view-activity-action');
+ await panelPO.enterTitle('testcase: 608aa47c-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '608aa47c-view',
+ });
+ await panelPO.checkCloseIfPresent(true);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-608aa47c-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-testing-view', componentSelector: 'app-testing-view'});
+ await expect(hostAppPO.getViewTabCount()).toBe(1);
+ });
+
+
+ it('should substitute path parameters with values from the intent qualifier [testcase: cc977da9-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-cc977da9-view-activity-action');
+ await panelPO.enterTitle('testcase: cc977da9-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: 'cc977da9-view',
+ qualifierParam1: 'e82bf49c4768',
+ qualifierParam2: '1b84a4a926f7'
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-cc977da9-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-cc977da9', componentSelector: 'app-view-cc977da9'});
+
+ const viewPO = new TestcaseCc977da9ViewPO();
+ const urlParams = await viewPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ a: 'e82bf49c4768',
+ b: '1b84a4a926f7',
+ });
+
+ const urlQueryParams = await viewPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toBeNull();
+ });
+
+ it('should substitute matrix and query parameters with values from the intent qualifier [testcase: b6a8fe23-view]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openViewOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-b6a8fe23-view-activity-action');
+ await panelPO.enterTitle('testcase: b6a8fe23-view');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: 'b6a8fe23-view',
+ qualifierParam1: 'd8b74df2c77d',
+ qualifierParam2: 'e60c81360bee',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-b6a8fe23-view-activity-action');
+ await action.click();
+
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-b6a8fe23', componentSelector: 'app-view-b6a8fe23'});
+
+ const viewPO = new TestcaseB6a8fe23ViewPO();
+ const urlParams = await viewPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ matrixParam1: 'd8b74df2c77d',
+ matrixParam2: 'eda2e91468e1',
+ pathParam1: 'd8b74df2c77d',
+ });
+
+ const urlQueryParams = await viewPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ queryParam1: 'e60c81360bee',
+ queryParam2: '1a3d3aaf937e'
+ });
+ });
+ });
+
+ describe('PopupOpenActivityAction', () => {
+
+ it('should allow navigation to private popups of the same application (implicit intent) [testcase: 1a90c8d2-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-1a90c8d2-popup-activity-action');
+ await panelPO.enterTitle('testcase: 1a90c8d2-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '1a90c8d2-popup',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-1a90c8d2-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-1a90c8d2', componentSelector: 'app-popup-1a90c8d2'});
+ });
+
+ it('should allow navigation to public popups of the same application (implicit intent) [testcase: 7330f506-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-7330f506-popup-activity-action');
+ await panelPO.enterTitle('testcase: 7330f506-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '7330f506-popup',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-7330f506-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-7330f506', componentSelector: 'app-popup-7330f506'});
+ });
+
+ it('should allow navigation to public popups of other applications [testcase: 924e114f777c]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-924e114f777c-popup-activity-action');
+ await panelPO.enterTitle('testcase: 924e114f777c-popup');
+ await panelPO.enterQualifier({
+ entity: 'communication',
+ action: 'create',
+ contactId: '5',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-924e114f777c-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'communication-app', popupCssClass: 'e2e-communication-create', componentSelector: 'app-communication-new-popup'});
+ });
+
+ it('should not allow navigation to public popups of other applications if missing the intent [testcase: 13370b2aa222]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-13370b2aa222-popup-activity-action');
+ await panelPO.enterTitle('testcase: 13370b2aa222-popup');
+ await panelPO.enterQualifier({
+ entity: 'communication',
+ action: 'create',
+ contactId: '999',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-13370b2aa222-popup-activity-action');
+ await action.click();
+
+ const notificationPO = await hostAppPO.findNotification('e2e-not-qualified');
+ await expect(notificationPO).not.toBeNull();
+ await expect(notificationPO.getSeverity()).toEqual('error');
+ await expectPopupToNotExist({symbolicAppName: 'communication-app', popupCssClass: 'e2e-communication-create'});
+ });
+
+ it('should not allow navigation to private popups of other application [testcase: cf12d60debc8]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-cf12d60debc8-popup-activity-action');
+ await panelPO.enterTitle('testcase: cf12d60debc8-popup');
+ await panelPO.enterQualifier({
+ entity: 'contact',
+ action: 'create',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-cf12d60debc8-popup-activity-action');
+ await action.click();
+
+ const notificationPO = await hostAppPO.findNotification('e2e-not-handled');
+ await expect(notificationPO).not.toBeNull();
+ await expect(notificationPO.getSeverity()).toEqual('error');
+ await expectPopupToNotExist({symbolicAppName: 'contact-app', popupCssClass: 'e2e-contact'});
+ });
+
+ it('should allow to provide query and matrix parameters [testcase: 9c5319f7-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-9c5319f7-popup-activity-action');
+ await panelPO.enterTitle('testcase: 9c5319f7-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '9c5319f7-popup'
+ });
+ await panelPO.enterMatrixParams({
+ mp1: '41ecdefec0a3',
+ mp2: 'da67bd554b36',
+ });
+ await panelPO.enterQueryParams({
+ qp1: '6378a62700d3',
+ qp2: 'c5a37d3660ba',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-9c5319f7-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-9c5319f7', componentSelector: 'app-popup-9c5319f7'});
+
+ const popupPO = new Testcase9c5319f7PopupPO();
+ const urlParams = await popupPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ mp1: '41ecdefec0a3',
+ mp2: 'da67bd554b36',
+ });
+
+ const urlQueryParams = await popupPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ qp1: '6378a62700d3',
+ qp2: 'c5a37d3660ba',
+ });
+ });
+
+ it('should allow to receive query and matrix parameters as specified in the manifest [testcase: 45dc693f-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-45dc693f-popup-activity-action');
+ await panelPO.enterTitle('testcase: 45dc693f-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '45dc693f-popup',
+ });
+
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-45dc693f-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-45dc693f', componentSelector: 'app-popup-45dc693f'});
+
+ const popupPO = new Testcase45dc693fPopupPO();
+ const urlParams = await popupPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ mp1: 'd52f5f88be27',
+ mp2: '01aa011fb2f4',
+ });
+
+ const urlQueryParams = await popupPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ qp1: 'f3227d5926c0',
+ qp2: '88a9cd0cb937',
+ });
+ });
+
+ it('should allow to merge query and matrix parameters provided from intent (overwrite) and manifest [testcase: f4286ac4-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-f4286ac4-popup-activity-action');
+ await panelPO.enterTitle('testcase: f4286ac4-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: 'f4286ac4-popup',
+ });
+ await panelPO.enterMatrixParams({
+ mp1: '557c7323a13c',
+ mp2: '67dbebc8dd7c',
+ });
+ await panelPO.enterQueryParams({
+ qp1: 'a3fa547519cf',
+ qp2: 'db03e5494054',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-f4286ac4-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-f4286ac4', componentSelector: 'app-popup-f4286ac4'});
+
+ const popupPO = new TestcaseF4286ac4PopupPO();
+ const urlParams = await popupPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ mp1: '557c7323a13c',
+ mp2: '67dbebc8dd7c',
+ mpx: 'f406422c77fc',
+ });
+
+ const urlQueryParams = await popupPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ qp1: 'a3fa547519cf',
+ qp2: 'db03e5494054',
+ qpx: '18c9d6c51b0c',
+ });
+ });
+
+ it('should substitute path parameters with values from the intent qualifier [testcase: 159913ad-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-159913ad-popup-activity-action');
+ await panelPO.enterTitle('testcase: 159913ad-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '159913ad-popup',
+ qualifierParam1: 'e82bf49c4768',
+ qualifierParam2: '1b84a4a926f7'
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-159913ad-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-159913ad', componentSelector: 'app-popup-159913ad'});
+
+ const popupPO = new Testcase159913adPopupPO();
+ const urlParams = await popupPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ a: 'e82bf49c4768',
+ b: '1b84a4a926f7',
+ });
+
+ const urlQueryParams = await popupPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toBeNull();
+ });
+
+ it('should substitute matrix and query parameters with values from the intent qualifier [testcase: 8a468258-popup]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-8a468258-popup-activity-action');
+ await panelPO.enterTitle('testcase: 8a468258-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ testcase: '8a468258-popup',
+ qualifierParam1: 'd8b74df2c77d',
+ qualifierParam2: 'e60c81360bee',
+ });
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-8a468258-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-popup-8a468258', componentSelector: 'app-popup-8a468258'});
+
+ const popupPO = new Testcase8a468258PopupPO();
+ const urlParams = await popupPO.getUrlParameters();
+ await expect(urlParams).toEqual({
+ matrixParam1: 'd8b74df2c77d',
+ matrixParam2: 'eda2e91468e1',
+ pathParam1: 'd8b74df2c77d',
+ });
+
+ const urlQueryParams = await popupPO.getUrlQueryParameters();
+ await expect(urlQueryParams).toEqual({
+ queryParam1: 'e60c81360bee',
+ queryParam2: '1a3d3aaf937e'
+ });
+ });
+
+
+ it('should close on focus lost [testcase: 9002887a395c]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-9002887a395c-popup-activity-action');
+ await panelPO.enterTitle('testcase: 9002887a395c-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ });
+ await panelPO.checkCloseOnFocusLost(true);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-9002887a395c-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+
+ // remove focus from the popup
+ await testingActivityPO.closePopupOpenActivityActionPanel();
+ await expectPopupToNotExist({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup'});
+ });
+
+ it('should not close on focus lost [testcase: 2e220909241f]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-2e220909241f-popup-activity-action');
+ await panelPO.enterTitle('testcase: 2e220909241f-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ });
+ await panelPO.checkCloseOnFocusLost(false);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-2e220909241f-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+
+ // remove focus from the popup
+ await testingActivityPO.closePopupOpenActivityActionPanel();
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+ });
+
+ it('should close on escape keystroke [testcase: e2a384f5294a]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-e2a384f5294a-popup-activity-action');
+ await panelPO.enterTitle('testcase: e2a384f5294a-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ });
+ await panelPO.checkCloseOnEscape(true);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-e2a384f5294a-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+
+ // send escape keystroke
+ await browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
+ await expectPopupToNotExist({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup'});
+ });
+
+ it('should not close on escape keystroke [testcase: 5aaa5df79806]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-5aaa5df79806-popup-activity-action');
+ await panelPO.enterTitle('testcase: 5aaa5df79806-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ });
+ await panelPO.checkCloseOnEscape(false);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-5aaa5df79806-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+
+ // send escape keystroke
+ await browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+ });
+
+ it('should close on grid layout change [testcase: c0ad45e6c742]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-c0ad45e6c742-popup-activity-action');
+ await panelPO.enterTitle('testcase: c0ad45e6c742-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ });
+ await panelPO.checkCloseOnFocusLost(false);
+ await panelPO.checkCloseOnGridLayoutChange(true);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-c0ad45e6c742-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+
+ // Make a grid layout change
+ await hostAppPO.moveActivitySash(100);
+ await expectPopupToNotExist({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup'});
+ });
+
+ it('should not close on grid layout change [testcase: a6919dfd2fb1]', async () => {
+ const testingActivityPO = new TestingActivityPO(E2E_TESTING_ACTIVITY_CONTEXT);
+ await testingActivityPO.navigateTo();
+
+ const panelPO = await testingActivityPO.openPopupOpenActivityActionPanel();
+ await panelPO.enterCssClass('fab fa-android e2e-a6919dfd2fb1-popup-activity-action');
+ await panelPO.enterTitle('testcase: a6919dfd2fb1-popup');
+ await panelPO.enterQualifier({
+ entity: 'testing',
+ });
+ await panelPO.checkCloseOnFocusLost(false);
+ await panelPO.checkCloseOnGridLayoutChange(false);
+ await panelPO.addAction();
+
+ // Execute the action
+ const action = await hostAppPO.findActivityAction('e2e-a6919dfd2fb1-popup-activity-action');
+ await action.click();
+
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+
+ // Make a grid layout change
+ await hostAppPO.moveActivitySash(100);
+ await expectPopupToShow({symbolicAppName: 'testing-app', popupCssClass: 'e2e-testing-popup', componentSelector: 'app-testing-popup'});
+ });
+ });
+ });
+});
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/message-box.e2e-spec.ts b/projects/e2e/workbench-application-platform/test-runner/src/message-box.e2e-spec.ts
new file mode 100644
index 000000000..d04d903f9
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/message-box.e2e-spec.ts
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { TestingViewPO } from './page-object/testing-view.po';
+import { HostAppPO } from './page-object/host-app.po';
+import { browser } from 'protractor';
+import { expectViewToNotExist, expectViewToShow } from './util/testing.util';
+import { noop } from 'rxjs';
+import { Testcase61097badMessageBoxPO } from './page-object/testcase-61097bad-msgbox.po';
+
+describe('MessageBox', () => {
+
+ const hostAppPO = new HostAppPO();
+ const testingViewPO = new TestingViewPO();
+
+ beforeEach(async () => {
+ await browser.get('/');
+ });
+
+ it('should show specified actions', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-a6cecbaa-msgbox');
+ const actions = {ok: 'OK', cancel: 'Cancel', ['127f38d1a966']: '9ce7b0679386'};
+ await msgboxPanelPO.enterActions(actions);
+
+ // close via ok action
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-a6cecbaa-msgbox');
+ await expect(msgboxPO.getActions()).toEqual(actions);
+ await msgboxPO.close('ok');
+ await expect(msgboxPanelPO.getCloseAction()).toEqual('ok');
+ }
+
+ // close via cancel action
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-a6cecbaa-msgbox');
+ await expect(msgboxPO.getActions()).toEqual(actions);
+ await msgboxPO.close('cancel');
+ await expect(msgboxPanelPO.getCloseAction()).toEqual('cancel');
+ }
+
+ // close via 127f38d1a966 action
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-a6cecbaa-msgbox');
+ await expect(msgboxPO.getActions()).toEqual(actions);
+ await msgboxPO.close('127f38d1a966');
+ await expect(msgboxPanelPO.getCloseAction()).toEqual('127f38d1a966');
+ }
+ });
+
+ it('should show specified text and title', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-c5dc3af5cd92-msgbox');
+ await msgboxPanelPO.enterText('01de40e40df3');
+ await msgboxPanelPO.enterTitle('976653342eef');
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+
+ await msgboxPanelPO.open();
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-c5dc3af5cd92-msgbox');
+ await expect(msgboxPO.getText()).toEqual('01de40e40df3');
+ await expect(msgboxPO.getTitle()).toEqual('976653342eef');
+ });
+
+ it('should show specified severity', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-fbf7ec24f2d4-msgbox');
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+
+ // info
+ await msgboxPanelPO.selectSeverity('info');
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-fbf7ec24f2d4-msgbox');
+ await expect(msgboxPO.getSeverity()).toEqual('info');
+ await msgboxPO.close('ok');
+ }
+
+ // warn
+ await msgboxPanelPO.selectSeverity('warn');
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-fbf7ec24f2d4-msgbox');
+ await expect(msgboxPO.getSeverity()).toEqual('warn');
+ await msgboxPO.close('ok');
+ }
+
+ // error
+ await msgboxPanelPO.selectSeverity('error');
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-fbf7ec24f2d4-msgbox');
+ await expect(msgboxPO.getSeverity()).toEqual('error');
+ await msgboxPO.close('ok');
+ }
+ });
+
+ it('should apply view modality [testcase: c91805e8]', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-dfa87d85eb1f-msgbox');
+ await msgboxPanelPO.selectModality('view');
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-dfa87d85eb1f-msgbox');
+ await expect(msgboxPO.isDisplayed()).toBeTruthy('expected messagebox to be displayed');
+ }
+
+ // test if other view can be opened via activity bar
+ {
+ await hostAppPO.clickActivityItem('e2e-activity-c91805e8');
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-c91805e8', componentSelector: 'app-view-c91805e8'});
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-dfa87d85eb1f-msgbox');
+ await expect(msgboxPO.isDisplayed()).toBeFalsy('expected messagebox not to be displayed');
+ }
+
+ // switch to disabled view
+ {
+ await hostAppPO.clickViewTab('e2e-testing-view');
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-testing-view', componentSelector: 'app-testing-view'});
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-dfa87d85eb1f-msgbox');
+ await expect(msgboxPO.isDisplayed()).toBeTruthy('expected messagebox to be displayed');
+ }
+ });
+
+ it('should apply application modality [testcase: c91805e8]', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-dfa87d85eb1f-msgbox');
+ await msgboxPanelPO.selectModality('application');
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+ await msgboxPanelPO.open();
+ {
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-dfa87d85eb1f-msgbox');
+ await expect(msgboxPO.isDisplayed()).toBeTruthy('expected messagebox to be displayed');
+ }
+
+ // test that other view cannot be opened via activity bar
+ {
+ await hostAppPO.clickActivityItem('e2e-activity-c91805e8').then(() => fail('expected activity item not to be clickable')).catch(noop);
+ await expectViewToNotExist({symbolicAppName: 'testing-app', viewCssClass: 'e2e-view-c91805e8'});
+ await expectViewToShow({symbolicAppName: 'testing-app', viewCssClass: 'e2e-testing-view', componentSelector: 'app-testing-view'});
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-dfa87d85eb1f-msgbox');
+ await expect(msgboxPO.isDisplayed()).toBeTruthy('expected messagebox to be displayed');
+ }
+ });
+
+ it('should make text selectable', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-cc1bea0bdaa0-msgbox');
+ await msgboxPanelPO.enterText('some selectable text');
+ await msgboxPanelPO.checkContentSelectable(true);
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+
+ await msgboxPanelPO.open();
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-cc1bea0bdaa0-msgbox');
+ await expect(msgboxPO.isContentSelectable()).toBeTruthy('expected be seletable');
+ });
+
+ it('should make text not selectable', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterCssClass('e2e-cc1bea0bdaa0-msgbox');
+ await msgboxPanelPO.enterText('some selectable text');
+ await msgboxPanelPO.checkContentSelectable(false);
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+
+ await msgboxPanelPO.open();
+ const msgboxPO = await hostAppPO.findMessageBox('e2e-cc1bea0bdaa0-msgbox');
+ await expect(msgboxPO.isContentSelectable()).toBeFalsy('expected not be seletable');
+ });
+
+ it('should show a custom message box [testcase: 61097bad]', async () => {
+ await testingViewPO.navigateTo();
+ const msgboxPanelPO = await testingViewPO.openMessageBoxPanel();
+
+ await msgboxPanelPO.enterQualifier({'type': 'list'});
+ await msgboxPanelPO.enterCssClass('e2e-61097bad-msgbox');
+ await msgboxPanelPO.enterPayload({'items': ['a', 'b', 'c']});
+ await msgboxPanelPO.enterActions({ok: 'OK'});
+
+ await msgboxPanelPO.open();
+ const msgboxPO = new Testcase61097badMessageBoxPO('e2e-61097bad-msgbox');
+ await expect(msgboxPO.getItems()).toEqual(['a', 'b', 'c']);
+ });
+});
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/notifcation-box.e2e-spec.ts b/projects/e2e/workbench-application-platform/test-runner/src/notifcation-box.e2e-spec.ts
new file mode 100644
index 000000000..4c4c4b6cf
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/notifcation-box.e2e-spec.ts
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { TestingViewPO } from './page-object/testing-view.po';
+import { HostAppPO } from './page-object/host-app.po';
+import { browser } from 'protractor';
+import { Testcase61097badNotificationPO } from './page-object/testcase-61097bad-notification.po';
+
+describe('Notification', () => {
+
+ const hostAppPO = new HostAppPO();
+ const testingViewPO = new TestingViewPO();
+
+ beforeEach(async () => {
+ await browser.get('/');
+ });
+
+ it('should show specified text and title', async () => {
+ await testingViewPO.navigateTo();
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+ await notificationPanelPO.enterCssClass('e2e-c5dc3af5cd92-notification');
+ await notificationPanelPO.enterText('01de40e40df3');
+ await notificationPanelPO.enterTitle('976653342eef');
+ await notificationPanelPO.show();
+
+ const notificationPO = await hostAppPO.findNotification('e2e-c5dc3af5cd92-notification');
+
+ await expect(notificationPO.getText()).toEqual('01de40e40df3');
+ await expect(notificationPO.getTitle()).toEqual('976653342eef');
+ });
+
+ it('should show specified severity', async () => {
+ await testingViewPO.navigateTo();
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+ await notificationPanelPO.enterCssClass('e2e-fbf7ec24f2d4-notification');
+
+ // info
+ await notificationPanelPO.selectSeverity('info');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getSeverity()).toEqual('info');
+ await notificationPO.close();
+ }
+
+ // warn
+ await notificationPanelPO.selectSeverity('warn');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getSeverity()).toEqual('warn');
+ await notificationPO.close();
+ }
+
+ // error
+ await notificationPanelPO.selectSeverity('error');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getSeverity()).toEqual('error');
+ await notificationPO.close();
+ }
+ });
+
+ it('should apply specified duration', async () => {
+ await testingViewPO.navigateTo();
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+ await notificationPanelPO.enterCssClass('e2e-fbf7ec24f2d4-notification');
+
+ // short
+ await notificationPanelPO.selectDuration('short');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getDuration()).toEqual('short');
+ await notificationPO.close();
+ }
+
+ // medium
+ await notificationPanelPO.selectDuration('medium');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getDuration()).toEqual('medium');
+ await notificationPO.close();
+ }
+
+ // long
+ await notificationPanelPO.selectDuration('long');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getDuration()).toEqual('long');
+ await notificationPO.close();
+ }
+
+ // infinite
+ await notificationPanelPO.selectDuration('infinite');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await expect(notificationPO.getDuration()).toBeNull();
+ await notificationPO.close();
+ }
+ });
+
+ it('should allow to close a notification', async () => {
+ await testingViewPO.navigateTo();
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+ await notificationPanelPO.enterCssClass('e2e-fbf7ec24f2d4-notification');
+
+ await notificationPanelPO.show();
+ await expect(hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification')).not.toBeNull('expected notification to show');
+
+ const notificationPO = await hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification');
+ await notificationPO.close();
+ await expect(hostAppPO.findNotification('e2e-fbf7ec24f2d4-notification')).toBeNull('expected notification to be closed');
+ });
+
+ it('should allow to show multiple notifications', async () => {
+ await testingViewPO.navigateTo();
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+
+ await notificationPanelPO.enterCssClass('e2e-6dbbd7fe35c0-notification');
+ await notificationPanelPO.show();
+
+ await notificationPanelPO.enterCssClass('e2e-b5e3e1a38c3d-notification');
+ await notificationPanelPO.show();
+
+ await expect(hostAppPO.findNotification('e2e-6dbbd7fe35c0-notification')).not.toBeNull('expected notification \'6dbbd7fe35c0\' to show');
+ await expect(hostAppPO.findNotification('e2e-b5e3e1a38c3d-notification')).not.toBeNull('expected notification \'b5e3e1a38c3d\' to show');
+ });
+
+ it('should allow to replace notifications', async () => {
+ await testingViewPO.navigateTo();
+
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+
+ await notificationPanelPO.enterCssClass('e2e-first-notification');
+ await notificationPanelPO.enterText('first');
+ await notificationPanelPO.enterGroup('group-identity');
+ await notificationPanelPO.show();
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-first-notification');
+ await expect(notificationPO.getText()).toEqual('first');
+ }
+
+ await notificationPanelPO.enterCssClass('e2e-second-notification');
+ await notificationPanelPO.enterText('second');
+ await notificationPanelPO.enterGroup('group-identity');
+ await notificationPanelPO.show();
+
+ await expect(hostAppPO.getNotificationCount()).toEqual(1);
+
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-first-notification');
+ await expect(notificationPO).toBeNull('expected notification \'first\' not to show');
+ }
+ {
+ const notificationPO = await hostAppPO.findNotification('e2e-second-notification');
+ await expect(notificationPO).not.toBeNull('expected notification \'second\' to show');
+ await expect(notificationPO.getText()).toEqual('second');
+ }
+ });
+
+ it('should show a custom notification [testcase: 61097bad]', async () => {
+ await testingViewPO.navigateTo();
+ const notificationPanelPO = await testingViewPO.openNotificationPanel();
+
+ await notificationPanelPO.enterQualifier({'type': 'list'});
+ await notificationPanelPO.enterCssClass('e2e-61097bad-notification');
+ await notificationPanelPO.enterPayload({'items': ['a', 'b', 'c']});
+
+ await notificationPanelPO.show();
+ const notificationPO = new Testcase61097badNotificationPO('e2e-61097bad-notification');
+ await expect(notificationPO.getItems()).toEqual(['a', 'b', 'c']);
+ });
+});
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/activity-actions-panel.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/activity-actions-panel.po.ts
new file mode 100644
index 000000000..1712676aa
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/activity-actions-panel.po.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { checkCheckbox, switchToIFrameContext } from '../util/testing.util';
+import { ActivityActionPO, HostAppPO } from './host-app.po';
+
+export class ActivityActionsPanelPo {
+
+ private _panel = $('app-activity-actions-panel');
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async checkShowUrlOpenActivityAction(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('input#show-url-open-activity-action'));
+ }
+
+ public async checkCustomActivityAction(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('input#show-custom-activity-action'));
+ }
+
+ public async findUrlOpenActivityAction(): Promise {
+ return new HostAppPO().findActivityAction('e2e-url-open-activity-action');
+ }
+
+ public async findCustomActivityAction(): Promise {
+ return new HostAppPO().findActivityAction('e2e-custom-activity-action');
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/activity-interaction-panel.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/activity-interaction-panel.po.ts
new file mode 100644
index 000000000..c4718effe
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/activity-interaction-panel.po.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+export class ActivityInteractionPanelPO {
+
+ private _panel = $('app-activity-interaction-panel');
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async enterTitle(title: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#title');
+ await inputField.clear();
+ await inputField.sendKeys(title);
+ return Promise.resolve();
+ }
+
+ public async enterItemText(itemText: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#item-text');
+ await inputField.clear();
+ await inputField.sendKeys(itemText);
+ return Promise.resolve();
+ }
+
+ public async enterItemCssClass(itemCssClass: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#item-css-class');
+ await inputField.clear();
+ await inputField.sendKeys(itemCssClass);
+ return Promise.resolve();
+ }
+
+ public async enterDeltaPx(deltaPx: number): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#panel-width-delta');
+ await inputField.clear();
+ await inputField.sendKeys(deltaPx);
+ return Promise.resolve();
+ }
+
+ public async getActiveLog(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const activeLog: string = await this._panel.$('textarea#active-log').getAttribute('value');
+ return activeLog.split(/\s+/).map(activeLogEntry => JSON.parse(activeLogEntry));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/host-app.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/host-app.po.ts
new file mode 100644
index 000000000..ee586aa36
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/host-app.po.ts
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $, $$, browser, ElementFinder, protractor } from 'protractor';
+import { getCssClasses, switchToMainContext } from '../util/testing.util';
+import { Duration, Severity } from '@scion/workbench-application-platform.api';
+import { ISize } from 'selenium-webdriver';
+
+export class HostAppPO {
+
+ /**
+ * Finds the view tab which has given CSS class set.
+ * If not found, the promise resolves to `null`.
+ */
+ public async findViewTab(cssClass: string): Promise {
+ await switchToMainContext();
+ const viewTabFinder = $(`wb-view-tab.${cssClass}`);
+
+ const exists = await viewTabFinder.isPresent();
+ if (!exists) {
+ return null;
+ }
+
+ return new class implements ViewTabPO {
+ async click(): Promise {
+ await switchToMainContext();
+ await viewTabFinder.click();
+ }
+
+ async close(): Promise {
+ await switchToMainContext();
+
+ // hover the view-tab to make the close button visible
+ await browser.actions().mouseMove(viewTabFinder).perform();
+ await viewTabFinder.$('.e2e-close').click();
+ }
+
+ async getTitle(): Promise {
+ await switchToMainContext();
+ return viewTabFinder.$('.e2e-title').getText();
+ }
+
+ async getHeading(): Promise {
+ await switchToMainContext();
+ return viewTabFinder.$('.e2e-heading').getText();
+ }
+
+ async isDirty(): Promise {
+ await switchToMainContext();
+ const element = viewTabFinder.$('.e2e-dirty');
+ return await element.isPresent() && await element.isDisplayed();
+ }
+
+ async isClosable(): Promise {
+ await switchToMainContext();
+ const element = viewTabFinder.$('.e2e-close');
+ return await element.isPresent() && await element.isDisplayed();
+ }
+ };
+ }
+
+ /**
+ * Returns the number of view tabs.
+ */
+ public async getViewTabCount(): Promise {
+ await switchToMainContext();
+ return $$('wb-view-tab').count();
+ }
+
+ /**
+ * Returns the number of notifications.
+ */
+ public async getNotificationCount(): Promise {
+ await switchToMainContext();
+ return $$('wb-notification').count();
+ }
+
+ /**
+ * Finds the notification which has given CSS class set.
+ * If not found, the promise resolves to `null`.
+ */
+ public async findNotification(cssClass: string): Promise {
+ await switchToMainContext();
+ const notificationFinder = $(`wb-notification.${cssClass}`);
+
+ const exists = await notificationFinder.isPresent();
+ if (!exists) {
+ return null;
+ }
+
+ return new class implements NotificationPO {
+ async getTitle(): Promise {
+ await switchToMainContext();
+ return notificationFinder.$('.e2e-title').getText();
+ }
+
+ async getText(): Promise {
+ await switchToMainContext();
+ return notificationFinder.$('.e2e-text').getText();
+ }
+
+ async getSeverity(): Promise {
+ await switchToMainContext();
+
+ const cssClasses = await getCssClasses(notificationFinder);
+ if (cssClasses.includes('e2e-severity-info')) {
+ return 'info';
+ }
+ else if (cssClasses.includes('e2e-severity-warn')) {
+ return 'warn';
+ }
+ else if (cssClasses.includes('e2e-severity-error')) {
+ return 'error';
+ }
+ return null;
+ }
+
+ async getDuration(): Promise {
+ await switchToMainContext();
+
+ const cssClasses = await getCssClasses(notificationFinder);
+ if (cssClasses.includes('e2e-duration-short')) {
+ return 'short';
+ }
+ else if (cssClasses.includes('e2e-duration-medium')) {
+ return 'medium';
+ }
+ else if (cssClasses.includes('e2e-duration-long')) {
+ return 'long';
+ }
+ else if (cssClasses.includes('infinite')) {
+ return 'infinite';
+ }
+ return null;
+ }
+
+ async close(): Promise {
+ await switchToMainContext();
+ await notificationFinder.$('.e2e-close').click();
+ // wait until the animation completes
+ await browser.wait(protractor.ExpectedConditions.stalenessOf(notificationFinder), 5000);
+ }
+ };
+ }
+
+ /**
+ * Finds the notification which has given CSS class set.
+ * If not found, the promise resolves to `null`.
+ */
+ public async findMessageBox(cssClass: string): Promise {
+ await switchToMainContext();
+ const msgboxFinder = $(`wb-message-box.${cssClass}`);
+
+ const exists = await msgboxFinder.isPresent();
+ if (!exists) {
+ return null;
+ }
+
+ return new class implements MessageBoxPO {
+ async getTitle(): Promise {
+ await switchToMainContext();
+ return msgboxFinder.$('.e2e-title').getText();
+ }
+
+ async getText(): Promise {
+ await switchToMainContext();
+ return msgboxFinder.$('.e2e-text').getText();
+ }
+
+ async getSeverity(): Promise {
+ await switchToMainContext();
+ const cssClasses = await getCssClasses(msgboxFinder);
+ if (cssClasses.includes('e2e-severity-info')) {
+ return 'info';
+ }
+ else if (cssClasses.includes('e2e-severity-warn')) {
+ return 'warn';
+ }
+ else if (cssClasses.includes('e2e-severity-error')) {
+ return 'error';
+ }
+ return null;
+ }
+
+ async getModality(): Promise<'application' | 'view' | null> {
+ await switchToMainContext();
+ const cssClasses = await getCssClasses(msgboxFinder);
+ if (cssClasses.includes('e2e-modality-application')) {
+ return 'application';
+ }
+ else if (cssClasses.includes('e2e-severity-view')) {
+ return 'view';
+ }
+ return null;
+ }
+
+ async isContentSelectable(): Promise {
+ await switchToMainContext();
+
+ const text = await msgboxFinder.$('.e2e-text').getText();
+
+ await browser.actions().mouseMove(msgboxFinder.$('.e2e-text')).perform();
+ await browser.actions().doubleClick().perform();
+ const selection: string = await browser.executeScript('return window.getSelection().toString();') as string;
+
+ return selection && selection.length && text.includes(selection);
+ }
+
+ async getActions(): Promise<{ [key: string]: string }> {
+ await switchToMainContext();
+ const actions: { [key: string]: string } = {};
+
+ const actionsFinder = msgboxFinder.$$('button.e2e-action');
+ const count = await actionsFinder.count();
+ for (let i = 0; i < count; i++) {
+ const action: ElementFinder = await actionsFinder.get(i);
+ const cssClasses = await getCssClasses(action);
+ const actionKey = cssClasses.find(candidate => candidate.startsWith('e2e-action-key-'));
+ actions[actionKey.substr('e2e-action-key-'.length)] = await action.getText();
+ }
+
+ return actions;
+ }
+
+ async close(action: string): Promise {
+ await switchToMainContext();
+ await msgboxFinder.$(`button.e2e-action.e2e-action-key-${action}`).click();
+ // wait until the animation completes
+ await browser.wait(protractor.ExpectedConditions.stalenessOf(msgboxFinder), 5000);
+ }
+
+ async isDisplayed(): Promise {
+ await switchToMainContext();
+ return msgboxFinder.isDisplayed();
+ }
+ };
+ }
+
+ /**
+ * Clicks the activity item which has given CSS class set.
+ *
+ * The promise returned is rejected if not found.
+ */
+ public async clickActivityItem(cssClass: string): Promise {
+ await switchToMainContext();
+ const activityItemPO = await this.findActivityItem(cssClass);
+ if (activityItemPO === null) {
+ return Promise.reject(`Activity item not found [cssClass=${cssClass}]`);
+ }
+ await activityItemPO.click();
+ }
+
+ /**
+ * Clicks the view tab which has given CSS class set.
+ *
+ * The promise returned is rejected if not found.
+ */
+ public async clickViewTab(cssClass: string): Promise {
+ await switchToMainContext();
+ const viewTabPO = await this.findViewTab(cssClass);
+ if (viewTabPO === null) {
+ return Promise.reject(`View tab not found [cssClass=${cssClass}]`);
+ }
+ await viewTabPO.click();
+ }
+
+ /**
+ * Finds the activity item which has given CSS class set.
+ * If not found, the promise resolves to `null`.
+ */
+ public async findActivityItem(cssClass: string): Promise {
+ await switchToMainContext();
+ const activityItemFinder = $(`wb-activity-part .e2e-activity-bar a.e2e-activity-item.${cssClass}`);
+ const activityPanelFinder = $(`wb-activity-part .e2e-activity-panel.${cssClass}`);
+
+ const exists = await activityItemFinder.isPresent();
+ if (!exists) {
+ return null;
+ }
+
+ return new class implements ActivityItemPO {
+ async getTitle(): Promise {
+ await switchToMainContext();
+ return await activityItemFinder.getAttribute('title');
+ }
+
+ async getText(): Promise {
+ await switchToMainContext();
+ return await activityItemFinder.getText();
+ }
+
+ async getCssClasses(): Promise {
+ await switchToMainContext();
+ return await getCssClasses(activityItemFinder);
+ }
+
+ async click(): Promise {
+ await switchToMainContext();
+
+ const cssClasses = await this.getCssClasses();
+ const closePanel = cssClasses.includes('e2e-active');
+ await activityItemFinder.click();
+ // wait until the animation completes
+ closePanel && await browser.wait(protractor.ExpectedConditions.stalenessOf(activityPanelFinder), 5000);
+ }
+ };
+ }
+
+ /**
+ * Finds the activity panel which has given CSS class set.
+ * If not found, the promise resolves to `null`.
+ */
+ public async findActivityPanel(cssClass: string): Promise {
+ await switchToMainContext();
+ const activityPanelFinder = $(`wb-activity-part .e2e-activity-panel.${cssClass}`);
+
+ const exists = await activityPanelFinder.isPresent();
+ if (!exists) {
+ return null;
+ }
+
+ return new class implements ActivityPanelPO {
+ async getTitle(): Promise {
+ await switchToMainContext();
+ return activityPanelFinder.$('.e2e-activity-title').getText();
+ }
+
+ async getSize(): Promise {
+ await switchToMainContext();
+ return activityPanelFinder.getSize();
+ }
+
+ async getCssClasses(): Promise {
+ await switchToMainContext();
+ return getCssClasses(activityPanelFinder);
+ }
+ };
+ }
+
+ /**
+ * Enlarges or shrinks the activity panel.
+ */
+ public async moveActivitySash(delta: number): Promise {
+ await switchToMainContext();
+ await browser.actions().mouseMove($('div.e2e-activity-sash'), {x: 0, y: 0}).perform();
+ await browser.actions()
+ .mouseDown()
+ .mouseMove({x: delta, y: 0})
+ .mouseUp()
+ .perform();
+ }
+
+ /**
+ * Finds the activity action which has given CSS class set.
+ */
+ public async findActivityAction(cssClass: string): Promise {
+ const actionFinder = $(`wb-activity-part .e2e-activity-actions .${cssClass}`);
+
+ return new class implements ActivityActionPO {
+ async isPresent(): Promise {
+ await switchToMainContext();
+ return actionFinder.isPresent();
+ }
+
+ async click(): Promise {
+ await switchToMainContext();
+ await actionFinder.click();
+ }
+ };
+ }
+}
+
+export interface ViewTabPO {
+ getTitle(): Promise;
+
+ getHeading(): Promise;
+
+ isDirty(): Promise;
+
+ isClosable(): Promise;
+
+ close(): Promise;
+
+ click(): Promise;
+}
+
+export interface NotificationPO {
+ getTitle(): Promise;
+
+ getText(): Promise;
+
+ getSeverity(): Promise;
+
+ getDuration(): Promise;
+
+ close(): Promise;
+}
+
+export interface MessageBoxPO {
+ getTitle(): Promise;
+
+ getText(): Promise;
+
+ getSeverity(): Promise;
+
+ getModality(): Promise<'view' | 'application' | null>;
+
+ isContentSelectable(): Promise;
+
+ getActions(): Promise<{ [key: string]: string }>;
+
+ close(action: string): Promise;
+
+ isDisplayed(): Promise;
+}
+
+/**
+ * Represents an activity item in the activity bar.
+ */
+export interface ActivityItemPO {
+ getTitle(): Promise;
+
+ getText(): Promise;
+
+ getCssClasses(): Promise;
+
+ click(): Promise;
+}
+
+/**
+ * Represents an activity panel in the activity part.
+ */
+export interface ActivityPanelPO {
+ getTitle(): Promise;
+
+ getSize(): Promise;
+
+ getCssClasses(): Promise;
+}
+
+/*
+* Represents a clickable activity action
+*/
+export interface ActivityActionPO {
+ isPresent(): Promise;
+
+ click(): Promise;
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/message-box-panel.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/message-box-panel.po.ts
new file mode 100644
index 000000000..0bf001f91
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/message-box-panel.po.ts
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $, browser, protractor } from 'protractor';
+import { checkCheckbox, selectOption, switchToIFrameContext, switchToMainContext } from '../util/testing.util';
+import { Qualifier, Severity } from '@scion/workbench-application-platform.api';
+import { SciParamsEnterPanelPO } from './sci-params-enter.po';
+
+export class MessageBoxPanelPO {
+
+ private _panel = $('app-message-box-panel');
+ private _cssClasses: string[] = [];
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async enterQualifier(qualifier: Qualifier): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(qualifier, this._panel.$('.e2e-qualifier-panel'));
+ }
+
+ public async enterTitle(label: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#title');
+ await inputField.clear();
+ await inputField.sendKeys(label);
+ return Promise.resolve();
+ }
+
+ public async enterText(text: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#text');
+ await inputField.clear();
+ await inputField.sendKeys(text);
+ return Promise.resolve();
+ }
+
+ public async selectSeverity(value: Severity): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await selectOption(value, this._panel.$('select#severity'));
+ }
+
+ public async selectModality(value: 'view' | 'application'): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await selectOption(value, this._panel.$('select#modality'));
+ }
+
+ public async checkContentSelectable(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#content-selectable'));
+ }
+
+ public async enterCssClass(cssClass: string): Promise {
+ this._cssClasses = cssClass.split(/\s+/);
+
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#css-class');
+ await inputField.clear();
+ await inputField.sendKeys(cssClass);
+ return Promise.resolve();
+ }
+
+ /**
+ * Enters given JSON object into the payload field.
+ */
+ public async enterPayload(payload: any): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = $('#payload');
+ await inputField.clear();
+ await inputField.sendKeys(JSON.stringify(payload));
+ return Promise.resolve();
+ }
+
+ public async enterActions(actions: { [key: string]: string }): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(actions, this._panel.$('.e2e-action-panel'));
+ }
+
+ public async getCloseAction(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ return this._panel.$('output.e2e-close-action').getText();
+ }
+
+ public async open(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await this._panel.$('button.e2e-open').click();
+
+ // wait until the animation completes
+ if (this._cssClasses.length) {
+ await switchToMainContext();
+ await browser.wait(protractor.ExpectedConditions.elementToBeClickable($(`wb-message-box.${this._cssClasses.join('.')}`)), 5000);
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/notification-panel.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/notification-panel.po.ts
new file mode 100644
index 000000000..0d653fc37
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/notification-panel.po.ts
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $, browser, protractor } from 'protractor';
+import { selectOption, switchToIFrameContext, switchToMainContext } from '../util/testing.util';
+import { Duration, Qualifier, Severity } from '@scion/workbench-application-platform.api';
+import { SciParamsEnterPanelPO } from './sci-params-enter.po';
+
+export class NotificationPanelPO {
+
+ private _panel = $('app-notification-panel');
+ private _cssClasses: string[] = [];
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async enterQualifier(qualifier: Qualifier): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(qualifier, this._panel.$('.e2e-qualifier-panel'));
+ }
+
+ public async enterTitle(label: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#title');
+ await inputField.clear();
+ await inputField.sendKeys(label);
+ return Promise.resolve();
+ }
+
+ public async enterText(text: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#text');
+ await inputField.clear();
+ await inputField.sendKeys(text);
+ return Promise.resolve();
+ }
+
+ public async selectSeverity(value: Severity): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await selectOption(value, this._panel.$('select#severity'));
+ }
+
+ public async selectDuration(value: Duration): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await selectOption(value, this._panel.$('select#duration'));
+ }
+
+ public async enterGroup(group: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#group');
+ await inputField.clear();
+ await inputField.sendKeys(group);
+ return Promise.resolve();
+ }
+
+ public async enterCssClass(cssClass: string): Promise {
+ this._cssClasses = cssClass.split(/\s+/);
+
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#css-class');
+ await inputField.clear();
+ await inputField.sendKeys(cssClass);
+ return Promise.resolve();
+ }
+
+ /**
+ * Enters given JSON object into the payload field.
+ */
+ public async enterPayload(payload: any): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = $('#payload');
+ await inputField.clear();
+ await inputField.sendKeys(JSON.stringify(payload));
+ return Promise.resolve();
+ }
+
+ public async show(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await this._panel.$('button.e2e-show').click();
+
+ // wait until the animation completes
+ if (this._cssClasses.length) {
+ await switchToMainContext();
+ await browser.wait(protractor.ExpectedConditions.elementToBeClickable($(`wb-notification.${this._cssClasses.join('.')}`)), 5000);
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/popup-open-activity-action-panel.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/popup-open-activity-action-panel.po.ts
new file mode 100644
index 000000000..36ce60218
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/popup-open-activity-action-panel.po.ts
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { checkCheckbox, switchToIFrameContext } from '../util/testing.util';
+import { Qualifier } from '@scion/workbench-application-platform.api';
+import { Params } from '@angular/router';
+import { SciParamsEnterPanelPO } from './sci-params-enter.po';
+
+export class PopupOpenActivityActionPanelPO {
+
+ private _panel = $('app-popup-open-activity-action-panel');
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async enterLabel(label: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#label');
+ await inputField.clear();
+ await inputField.sendKeys(label);
+ return Promise.resolve();
+ }
+
+ public async enterTitle(label: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#title');
+ await inputField.clear();
+ await inputField.sendKeys(label);
+ return Promise.resolve();
+ }
+
+ public async enterCssClass(cssClass: string): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ const inputField = this._panel.$('input#css-class');
+ await inputField.clear();
+ await inputField.sendKeys(cssClass);
+ return Promise.resolve();
+ }
+
+ public async enterQualifier(qualifier: Qualifier): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(qualifier, this._panel.$('.e2e-qualifier-panel'));
+ }
+
+ public async enterMatrixParams(params: Params): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(params, this._panel.$('.e2e-matrix-params-panel'));
+ }
+
+ public async enterQueryParams(params: Params): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(params, this._panel.$('.e2e-query-params-panel'));
+ }
+
+ public async checkCloseOnFocusLost(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#onFocusLost'));
+ }
+
+ public async checkCloseOnEscape(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#onEscape'));
+ }
+
+ public async checkCloseOnGridLayoutChange(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#onGridLayoutChange'));
+ }
+
+ public async addAction(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await this._panel.$('button.e2e-add-action').click();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/popup-panel.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/popup-panel.po.ts
new file mode 100644
index 000000000..eab373e6a
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/popup-panel.po.ts
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { checkCheckbox, selectOption, switchToIFrameContext } from '../util/testing.util';
+import { Qualifier } from '@scion/workbench-application-platform.api';
+import { Params } from '@angular/router';
+import { SciParamsEnterPanelPO } from './sci-params-enter.po';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+export class PopupPanelPO {
+
+ private _panel = $('app-popup-panel');
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async enterQualifier(qualifier: Qualifier): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(qualifier, this._panel.$('.e2e-qualifier-panel'));
+ }
+
+ public async enterMatrixParams(params: Params): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(params, this._panel.$('.e2e-matrix-params-panel'));
+ }
+
+ public async enterQueryParams(params: Params): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciParamsEnterPanelPO().enterParams(params, this._panel.$('.e2e-query-params-panel'));
+ }
+
+ public async selectPosition(value: 'north' | 'east' | 'south' | 'west'): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await selectOption(value, this._panel.$('select#position'));
+ }
+
+ public async checkCloseOnFocusLost(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#onFocusLost'));
+ }
+
+ public async checkCloseOnEscape(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#onEscape'));
+ }
+
+ public async checkCloseOnGridLayoutChange(check: boolean): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await checkCheckbox(check, this._panel.$('#onGridLayoutChange'));
+ }
+
+ public async execute(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await this._panel.$('button.e2e-execute').click();
+ }
+
+ public async getResult(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(this.iframeContext);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-result'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-accordion-p.o.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-accordion-p.o.ts
new file mode 100644
index 000000000..b60e0f786
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-accordion-p.o.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ElementFinder } from 'protractor';
+import { getCssClasses } from '../util/testing.util';
+
+export class SciAccordionPO {
+
+ /**
+ * Opens or closes the accordion item of given CSS class.
+ * If `open` is not specified, the item is toggled.
+ */
+ public async toggle(sciAccordionFinder: ElementFinder, cssClass: string, open?: boolean): Promise {
+ const accordionItem = sciAccordionFinder.$(`section.e2e-accordion-item.${cssClass}`);
+ const cssClasses = await getCssClasses(accordionItem);
+
+ if (open === undefined) {
+ open = !cssClasses.includes('e2e-expanded');
+ }
+
+ const doOpen = open && !cssClasses.includes('e2e-expanded');
+ const doClose = !open && cssClasses.includes('e2e-expanded');
+
+ if (doOpen || doClose) {
+ await accordionItem.$('.e2e-accordion-item-header').click();
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-params-enter.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-params-enter.po.ts
new file mode 100644
index 000000000..72470b7af
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-params-enter.po.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ElementFinder } from 'protractor';
+
+export class SciParamsEnterPanelPO {
+
+ /**
+ * Allows to enter parameters into '' panel.
+ */
+ public async enterParams(params: Object, sciParamsEnterPanel: ElementFinder): Promise {
+ const addButton = sciParamsEnterPanel.$('button.e2e-add');
+ const lastKeyInput = sciParamsEnterPanel.$$('input.e2e-key').last();
+ const lastValueInput = sciParamsEnterPanel.$$('input.e2e-value').last();
+
+ for (const key of Object.keys(params)) {
+ await addButton.click();
+ await lastKeyInput.sendKeys(key);
+ await lastValueInput.sendKeys(`${params[key]}`);
+ }
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-property.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-property.po.ts
new file mode 100644
index 000000000..4a2324e31
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/sci-property.po.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { ElementFinder } from 'protractor';
+
+export class SciPropertyPanelPO {
+
+ /**
+ * Allows to read properties from '' panel.
+ */
+ public async readProperties(sciPropertyPanel: ElementFinder): Promise<{ [key: string]: string }> {
+ const keysFinder = sciPropertyPanel.$$('.e2e-key');
+ const valuesFinder = sciPropertyPanel.$$('.e2e-value');
+
+ const propertyCount = await keysFinder.count();
+ if (propertyCount === 0) {
+ return null;
+ }
+
+ const properties: { [key: string]: string } = {};
+ for (let i = 0; i < propertyCount; i++) {
+ const key = await keysFinder.get(i).getText();
+ const value = await valuesFinder.get(i).getText();
+ properties[key] = value;
+ }
+
+ return properties;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-159913ad-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-159913ad-popup.po.ts
new file mode 100644
index 000000000..9ddd09423
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-159913ad-popup.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-159913ad'];
+
+export class Testcase159913adPopupPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-28f32b51-activity.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-28f32b51-activity.po.ts
new file mode 100644
index 000000000..4e0ffe0bc
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-28f32b51-activity.po.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+const E2E_TESTING_ACTIVITY_CONTEXT: string[] = ['e2e-testing-app', 'e2e-activity', 'e2e-activity-28f32b51'];
+
+export class Testcase28f32b51ActivityPO {
+
+ public async readActiveLog(): Promise {
+ await switchToIFrameContext(E2E_TESTING_ACTIVITY_CONTEXT);
+ const activeLog: string = await $('textarea#active-log').getAttribute('value');
+ return activeLog.split(/\s+/).map(activeLogEntry => JSON.parse(activeLogEntry));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-28f32b51-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-28f32b51-view.po.ts
new file mode 100644
index 000000000..1c960c140
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-28f32b51-view.po.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-28f32b51'];
+
+export class Testcase28f32b51ViewPO {
+
+ public async readActiveLog(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ const activeLog: string = await $('textarea#active-log').getAttribute('value');
+ return activeLog.split(/\s+/).map(activeLogEntry => JSON.parse(activeLogEntry));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-45dc693f-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-45dc693f-popup.po.ts
new file mode 100644
index 000000000..a0abb0074
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-45dc693f-popup.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-45dc693f'];
+
+export class Testcase45dc693fPopupPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-4a3a8984-activity-po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-4a3a8984-activity-po.ts
new file mode 100644
index 000000000..a9b61c8c9
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-4a3a8984-activity-po.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+const E2E_TESTING_ACTIVITY_CONTEXT: string[] = ['e2e-testing-app', 'e2e-activity', 'e2e-activity-4a3a8984'];
+
+export class Testcase4a3a8984ActivityPO {
+
+ public async getComponentInstanceUuid(): Promise {
+ await switchToIFrameContext(E2E_TESTING_ACTIVITY_CONTEXT);
+ return await $('.e2e-component-instance-uuid').getText();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-56657ad1-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-56657ad1-view.po.ts
new file mode 100644
index 000000000..672a93d60
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-56657ad1-view.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-56657ad1'];
+
+export class Testcase56657ad1ViewPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-activity.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-activity.po.ts
new file mode 100644
index 000000000..71a1b154b
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-activity.po.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { browser, protractor } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+const E2E_TESTING_ACTIVITY_CONTEXT: string[] = ['e2e-testing-app', 'e2e-activity', 'e2e-activity-5782ab19'];
+
+export class Testcase5782ab19ActivityPO {
+
+ /**
+ * Returns if given element is the active element.
+ */
+ public async isActiveElement(fieldId: string): Promise {
+ await switchToIFrameContext(E2E_TESTING_ACTIVITY_CONTEXT);
+ const activeElementId = await browser.driver.switchTo().activeElement().getAttribute('id');
+ return activeElementId === fieldId;
+ }
+
+ public async pressTab(): Promise {
+ await switchToIFrameContext(E2E_TESTING_ACTIVITY_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.TAB).perform();
+ }
+
+ public async pressShiftTab(): Promise {
+ await switchToIFrameContext(E2E_TESTING_ACTIVITY_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.SHIFT, protractor.Key.TAB, protractor.Key.SHIFT).perform();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-popup.po.ts
new file mode 100644
index 000000000..ef1bdc653
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-popup.po.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { browser, protractor } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-5782ab19'];
+
+export class Testcase5782ab19PopupPO {
+
+ /**
+ * Returns if given element is the active element.
+ */
+ public async isActiveElement(fieldId: string): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ const activeElementId = await browser.driver.switchTo().activeElement().getAttribute('id');
+ return activeElementId === fieldId;
+ }
+
+ public async pressTab(): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.TAB).perform();
+ }
+
+ public async pressShiftTab(): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.SHIFT, protractor.Key.TAB, protractor.Key.SHIFT).perform();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-view.po.ts
new file mode 100644
index 000000000..452410797
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-5782ab19-view.po.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { browser, protractor } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-5782ab19'];
+
+export class Testcase5782ab19ViewPO {
+
+ /**
+ * Returns if given element is the active element.
+ */
+ public async isActiveElement(fieldId: string): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ const activeElementId = await browser.driver.switchTo().activeElement().getAttribute('id');
+ return activeElementId === fieldId;
+ }
+
+ public async pressTab(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.TAB).perform();
+ }
+
+ public async pressShiftTab(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.SHIFT, protractor.Key.TAB, protractor.Key.SHIFT).perform();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-61097bad-msgbox.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-61097bad-msgbox.po.ts
new file mode 100644
index 000000000..892983bb5
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-61097bad-msgbox.po.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToMainContext } from '../util/testing.util';
+
+export class Testcase61097badMessageBoxPO {
+
+ constructor(private _msgboxCssClass: string) {
+ }
+
+ public async getItems(): Promise {
+ await switchToMainContext();
+ const msgboxFinder = $(`wb-message-box.${this._msgboxCssClass} app-list-messagebox`);
+ const itemFinder = msgboxFinder.$$('.e2e-item');
+ const itemCount = await itemFinder.count();
+
+ const items: string[] = [];
+ for (let i = 0; i < itemCount; i++) {
+ items.push(await itemFinder.get(i).getText());
+ }
+ return items;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-61097bad-notification.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-61097bad-notification.po.ts
new file mode 100644
index 000000000..cf3b1494c
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-61097bad-notification.po.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToMainContext } from '../util/testing.util';
+
+export class Testcase61097badNotificationPO {
+
+ constructor(private _notificationCssClass: string) {
+ }
+
+ public async getItems(): Promise {
+ await switchToMainContext();
+ const notificationFinder = $(`wb-notification.${this._notificationCssClass} app-list-notification`);
+ const itemFinder = notificationFinder.$$('.e2e-item');
+ const itemCount = await itemFinder.count();
+
+ const items: string[] = [];
+ for (let i = 0; i < itemCount; i++) {
+ items.push(await itemFinder.get(i).getText());
+ }
+ return items;
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-68f302b4-view-po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-68f302b4-view-po.ts
new file mode 100644
index 000000000..88a30cd14
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-68f302b4-view-po.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+import { ViewNavigationPanelPO } from './view-navigation-panel.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-68f302b4'];
+
+export class Testcase68f302b4ViewPO {
+
+ public readonly viewNavigationPanelPO = new ViewNavigationPanelPO(E2E_TESTING_VIEW_CONTEXT);
+
+ public async getAppInstanceUuid(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return await $('.e2e-app-instance-uuid').getText();
+ }
+
+ public async getComponentInstanceUuid(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return await $('.e2e-component-instance-uuid').getText();
+ }
+
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-8a468258-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-8a468258-popup.po.ts
new file mode 100644
index 000000000..d0505aa17
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-8a468258-popup.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-8a468258'];
+
+export class Testcase8a468258PopupPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-9c5319f7-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-9c5319f7-popup.po.ts
new file mode 100644
index 000000000..07dbf17ed
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-9c5319f7-popup.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-9c5319f7'];
+
+export class Testcase9c5319f7PopupPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-a686d615-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-a686d615-view.po.ts
new file mode 100644
index 000000000..8772d8abd
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-a686d615-view.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-a686d615'];
+
+export class TestcaseA686d615ViewPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-b6a8fe23-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-b6a8fe23-view.po.ts
new file mode 100644
index 000000000..21cca67f5
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-b6a8fe23-view.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-b6a8fe23'];
+
+export class TestcaseB6a8fe23ViewPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-c8e40918-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-c8e40918-view.po.ts
new file mode 100644
index 000000000..38e330c6f
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-c8e40918-view.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-c8e40918'];
+
+export class TestcaseC8e40918ViewPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-cc977da9-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-cc977da9-view.po.ts
new file mode 100644
index 000000000..ae58d8f97
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-cc977da9-view.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-cc977da9'];
+
+export class TestcaseCc977da9ViewPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-f4286ac4-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-f4286ac4-popup.po.ts
new file mode 100644
index 000000000..12dcd9e14
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-f4286ac4-popup.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-f4286ac4'];
+
+export class TestcaseF4286ac4PopupPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-fc077b32-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-fc077b32-popup.po.ts
new file mode 100644
index 000000000..5d88d75c4
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-fc077b32-popup.po.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { getCssClasses, switchToIFrameContext, switchToMainContext } from '../util/testing.util';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-popup-fc077b32'];
+
+export class Testcasefc077b32PopupPO {
+
+ public async getPosition(): Promise<'north' | 'east' | 'south' | 'west' | null> {
+ await switchToMainContext();
+ const cssClasses = await getCssClasses($(`.wb-popup.e2e-popup-fc077b32`));
+ if (cssClasses.includes('e2e-position-north')) {
+ return 'north';
+ }
+ else if (cssClasses.includes('e2e-position-east')) {
+ return 'east';
+ }
+ else if (cssClasses.includes('e2e-position-south')) {
+ return 'south';
+ }
+ else if (cssClasses.includes('e2e-position-west')) {
+ return 'west';
+ }
+ return null;
+ }
+
+ public async close(): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ await $('button#e2e-close').click();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-ffd6a78f-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-ffd6a78f-view.po.ts
new file mode 100644
index 000000000..ab08159c2
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testcase-ffd6a78f-view.po.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-view-ffd6a78f'];
+
+export class TestcaseFfd6a78fViewPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-activity.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-activity.po.ts
new file mode 100644
index 000000000..2abe95ecf
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-activity.po.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { HostAppPO } from './host-app.po';
+import { SciAccordionPO } from './sci-accordion-p.o';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+import { ActivityInteractionPanelPO } from './activity-interaction-panel.po';
+import { ActivityActionsPanelPo } from './activity-actions-panel.po';
+import { ViewOpenActivityActionPanelPO } from './view-open-activity-action-panel.po';
+import { PopupOpenActivityActionPanelPO } from './popup-open-activity-action-panel.po';
+
+export class TestingActivityPO {
+
+ constructor(public iframeContext: string[]) {
+ }
+
+ public async navigateTo(): Promise {
+ await new HostAppPO().clickActivityItem('e2e-testing-activity');
+ }
+
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(this.iframeContext);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(this.iframeContext);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+
+ public async openActivityInteractionPanel(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciAccordionPO().toggle($('sci-accordion'), 'e2e-activity-interaction-panel', true);
+ return new ActivityInteractionPanelPO(this.iframeContext);
+ }
+
+ public async openActivityActionsPanel(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciAccordionPO().toggle($('sci-accordion'), 'e2e-activity-actions-panel', true);
+ return new ActivityActionsPanelPo(this.iframeContext);
+ }
+
+ public async openViewOpenActivityActionPanel(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciAccordionPO().toggle($('sci-accordion'), 'e2e-add-view-open-activity-action', true);
+ return new ViewOpenActivityActionPanelPO(this.iframeContext);
+ }
+
+ public async openPopupOpenActivityActionPanel(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciAccordionPO().toggle($('sci-accordion'), 'e2e-add-popup-open-activity-action', true);
+ return new PopupOpenActivityActionPanelPO(this.iframeContext);
+ }
+
+ public async closePopupOpenActivityActionPanel(): Promise {
+ await switchToIFrameContext(this.iframeContext);
+ await new SciAccordionPO().toggle($('sci-accordion'), 'e2e-add-popup-open-activity-action', false);
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-popup.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-popup.po.ts
new file mode 100644
index 000000000..d94131aab
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-popup.po.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $, browser, protractor } from 'protractor';
+import { switchToIFrameContext } from '../util/testing.util';
+import { SciPropertyPanelPO } from './sci-property.po';
+
+const E2E_TESTING_POPUP_CONTEXT: string[] = ['e2e-testing-app', 'e2e-popup', 'e2e-testing-popup'];
+
+export class TestingPopupPO {
+
+ /**
+ * Reads URL parameters.
+ */
+ public async getUrlParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-params'));
+ }
+
+ /**
+ * Reads URL query parameters.
+ */
+ public async getUrlQueryParameters(): Promise<{ [key: string]: string }> {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ return new SciPropertyPanelPO().readProperties($('sci-property.e2e-url-query-params'));
+ }
+
+ /**
+ * Enters given JSON object into the result field.
+ */
+ public async enterResult(result: any): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ const inputField = $('textarea.e2e-result');
+ await inputField.clear();
+ await inputField.sendKeys(JSON.stringify(result));
+ return Promise.resolve();
+ }
+
+ /**
+ * Closes the popup and returns the result.
+ */
+ public async ok(): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ await $('button.e2e-ok').click();
+ }
+
+ /**
+ * Closes the popup without returning a result.
+ */
+ public async escape(): Promise {
+ await switchToIFrameContext(E2E_TESTING_POPUP_CONTEXT);
+ await browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
+ }
+}
diff --git a/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-view.po.ts b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-view.po.ts
new file mode 100644
index 000000000..7f8b5dd3a
--- /dev/null
+++ b/projects/e2e/workbench-application-platform/test-runner/src/page-object/testing-view.po.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 Swiss Federal Railways
+ *
+ * This program and the accompanying materials are made
+ * available under the terms from the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+import { $ } from 'protractor';
+import { ViewInteractionPanelPO } from './view-interaction-panel.po';
+import { ViewNavigationPanelPO } from './view-navigation-panel.po';
+import { HostAppPO } from './host-app.po';
+import { PopupPanelPO } from './popup-panel.po';
+import { SciAccordionPO } from './sci-accordion-p.o';
+import { switchToIFrameContext } from '../util/testing.util';
+import { MessageBoxPanelPO } from './message-box-panel.po';
+import { NotificationPanelPO } from './notification-panel.po';
+
+const E2E_TESTING_VIEW_CONTEXT: string[] = ['e2e-testing-app', 'e2e-view', 'e2e-testing-view'];
+
+export class TestingViewPO {
+
+ private _component = $('app-testing-view');
+
+ public async navigateTo(): Promise {
+ await new HostAppPO().clickActivityItem('e2e-open-testing-view');
+ }
+
+ public async openViewInteractionPanel(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ await new SciAccordionPO().toggle(this._component.$('sci-accordion'), 'e2e-view-interaction-panel', true);
+ return new ViewInteractionPanelPO(E2E_TESTING_VIEW_CONTEXT);
+ }
+
+ public async openViewNavigationPanel(): Promise {
+ await switchToIFrameContext(E2E_TESTING_VIEW_CONTEXT);
+ await new SciAccordionPO().toggle(this._component.$('sci-accordion'), 'e2e-view-navigation-panel', true);
+ return new ViewNavigationPanelPO(E2E_TESTING_VIEW_CONTEXT);
+ }
+
+ public async openPopupPanel(): Promise