From a11746659935e5c6f51d076924adbd7e7489d499 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Ka=C5=88ka?=
<124378142+LukasKanka@users.noreply.github.com>
Date: Tue, 15 Aug 2023 18:27:27 +0200
Subject: [PATCH] =?UTF-8?q?=C3=BAdr=C5=BEba?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
PWLukTS/.gitignore | 4 +
PWLukTS/.idea/.gitignore | 8 +
PWLukTS/.idea/PWLukTS.iml | 9 +
PWLukTS/.idea/misc.xml | 6 +
PWLukTS/.idea/modules.xml | 8 +
PWLukTS/.idea/vcs.xml | 7 +
...d716de9a-2387-475d-b8ff-c8380967c4a3.vsidx | Bin 0 -> 56597 bytes
PWLukTS/.vs/PWLukTS/v17/.wsuo | Bin 0 -> 14848 bytes
PWLukTS/.vs/VSWorkspaceState.json | 8 +
PWLukTS/.vs/slnx.sqlite | Bin 0 -> 102400 bytes
PWLukTS/README.md | 46 ++
PWLukTS/helpers.ts | 8 +
PWLukTS/package-lock.json | 67 +++
PWLukTS/package.json | 13 +
PWLukTS/page-objects/HomePage.ts | 83 ++++
PWLukTS/page-objects/LoginPage.ts | 7 +
PWLukTS/playwright.config.ts | 78 ++++
PWLukTS/tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
PWLukTS/tests/cookies.test.spec.ts | 19 +
PWLukTS/tests/example.spec.ts | 18 +
PWLukTS/tests/help.spec.ts | 10 +
PWLukTS/tests/login.spec.ts | 1 +
PWLukTS/tests/menuButton.test.spec.ts | 12 +
PWLukTS/tests/primary.menu.spec.ts | 29 ++
PWLukTS/tests/search.spec.ts | 10 +
PWLukTS/tests/social.test.spec.ts | 26 ++
PWLukTS/tests/test-1.spec.ts | 6 +
PWLukTS/tests/tittle.spec.ts | 9 +
PWLukTS/tests/vsechny.spec.ts | 89 ++++
PW_ZiveTS/.gitignore | 16 +
PW_ZiveTS/.idea/PW_ZiveTS.iml | 9 +
PW_ZiveTS/.idea/misc.xml | 6 +
PW_ZiveTS/.idea/modules.xml | 8 +
PW_ZiveTS/.idea/vcs.xml | 6 +
PW_ZiveTS/.idea/workspace.xml | 99 ++++
PW_ZiveTS/e2e/example.spec.ts | 18 +
PW_ZiveTS/package-lock.json | 67 +++
PW_ZiveTS/package.json | 16 +
PW_ZiveTS/playwright.config.ts | 90 ++++
PW_ZiveTS/test/aa.spec.ts | 26 ++
PW_ZiveTS/test/aaa.spec.ts | 17 +
PW_ZiveTS/test/example.spec.ts | 44 ++
.../tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
Playwright základy na GitHub/.gitignore | 4 +
.../.idea/.gitignore | 8 +
.../.idea/Playwright základy na GitHub.iml | 9 +
Playwright základy na GitHub/.idea/misc.xml | 6 +
.../.idea/modules.xml | 8 +
Playwright základy na GitHub/.idea/vcs.xml | 6 +
...Identifikácia elementov a akcie s nimi.md | 7 +
Playwright základy na GitHub/Async Await.md | 158 +++++++
Playwright základy na GitHub/README.MD | 44 ++
.../fixtures/basePages.ts | 0
.../package-lock.json | 63 +++
Playwright základy na GitHub/package.json | 12 +
.../page-objects copy/HomePage.ts | 45 ++
.../page-objects copy/LoginPage.ts | 71 +++
.../page-objects/HomePage.ts | 45 ++
.../page-objects/LoginPage.ts | 72 +++
.../playwright.config.ts | 77 +++
.../tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
.../tests/alerts.spec.ts | 0
.../tests/assertions.spec.ts | 27 ++
.../tests/elementState.spec.ts | 18 +
.../tests/example.spec copy.ts | 18 +
.../tests/example.spec.ts | 18 +
.../tests/home.spec.ts | 29 ++
.../tests/login.spec.ts | 79 ++++
.../tests/screenshots.spec.ts | 0
.../tests/tabs.spec.ts | 0
.../tests/upload.spec.ts | 0
Playwright základy na GitHub/Úkoly.md | 13 +
.../Čo je Page Object Model (POM).md | 9 +
.../...github/workflows/search_google.yml | 37 ++
Playwright_GH_TS/...github/workflows/zive.yml | 37 ++
Playwright_GH_TS/aaa/.gitignore | 4 +
Playwright_GH_TS/aaa/package-lock.json | 67 +++
Playwright_GH_TS/aaa/package.json | 13 +
Playwright_GH_TS/aaa/playwright.config.ts | 77 +++
.../aaa/tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
Playwright_GH_TS/aaa/tests/aa.spec.ts | 26 ++
Playwright_GH_TS/aaa/tests/aaa.spec.ts | 17 +
Playwright_GH_TS/aaa/tests/example.spec.ts | 18 +
Playwright_GH_TS/lukan/.gitignore | 7 +
Playwright_GH_TS/lukan/e2e/example.spec.ts | 18 +
Playwright_GH_TS/lukan/package-lock.json | 67 +++
Playwright_GH_TS/lukan/package.json | 13 +
.../lukan/page-objects/HomePage.ts | 62 +++
.../lukan/page-objects/LoginPage.ts | 5 +
Playwright_GH_TS/lukan/playwright.config.ts | 77 +++
.../tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
.../lukan/tests/cookies.test.spec.ts | 19 +
Playwright_GH_TS/lukan/tests/example.spec.ts | 18 +
Playwright_GH_TS/lukan/tests/login.spec.ts | 0
.../lukan/tests/menuButton.test.spec.ts | 14 +
Playwright_GH_TS/lukan/tests/search.spec.ts | 10 +
Playwright_GH_TS/lukan/tests/test-1.spec.ts | 6 +
Playwright_GH_TS/search_google/.gitignore | 10 +
.../search_google/package-lock.json | 67 +++
Playwright_GH_TS/search_google/package.json | 13 +
.../search_google/playwright.config.ts | 90 ++++
.../tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
.../search_google/tests/example.spec.ts | 33 ++
Playwright_GH_TS/zive.ts/.gitignore | 4 +
Playwright_GH_TS/zive.ts/package-lock.json | 67 +++
Playwright_GH_TS/zive.ts/package.json | 13 +
Playwright_GH_TS/zive.ts/playwright.config.ts | 90 ++++
.../tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
.../zive.ts/tests/example.spec.ts | 11 +
Playwright_GH_TS/zive/.gitignore | 7 +
Playwright_GH_TS/zive/e2e/example.spec.ts | 18 +
Playwright_GH_TS/zive/package-lock.json | 67 +++
Playwright_GH_TS/zive/package.json | 16 +
Playwright_GH_TS/zive/playwright.config.ts | 90 ++++
.../zive/tests-examples/demo-todo-app.spec.ts | 437 ++++++++++++++++++
Playwright_GH_TS/zive/tests/example.spec.ts | 44 ++
116 files changed, 6597 insertions(+)
create mode 100644 PWLukTS/.gitignore
create mode 100644 PWLukTS/.idea/.gitignore
create mode 100644 PWLukTS/.idea/PWLukTS.iml
create mode 100644 PWLukTS/.idea/misc.xml
create mode 100644 PWLukTS/.idea/modules.xml
create mode 100644 PWLukTS/.idea/vcs.xml
create mode 100644 PWLukTS/.vs/PWLukTS/FileContentIndex/d716de9a-2387-475d-b8ff-c8380967c4a3.vsidx
create mode 100644 PWLukTS/.vs/PWLukTS/v17/.wsuo
create mode 100644 PWLukTS/.vs/VSWorkspaceState.json
create mode 100644 PWLukTS/.vs/slnx.sqlite
create mode 100644 PWLukTS/README.md
create mode 100644 PWLukTS/helpers.ts
create mode 100644 PWLukTS/package-lock.json
create mode 100644 PWLukTS/package.json
create mode 100644 PWLukTS/page-objects/HomePage.ts
create mode 100644 PWLukTS/page-objects/LoginPage.ts
create mode 100644 PWLukTS/playwright.config.ts
create mode 100644 PWLukTS/tests-examples/demo-todo-app.spec.ts
create mode 100644 PWLukTS/tests/cookies.test.spec.ts
create mode 100644 PWLukTS/tests/example.spec.ts
create mode 100644 PWLukTS/tests/help.spec.ts
create mode 100644 PWLukTS/tests/login.spec.ts
create mode 100644 PWLukTS/tests/menuButton.test.spec.ts
create mode 100644 PWLukTS/tests/primary.menu.spec.ts
create mode 100644 PWLukTS/tests/search.spec.ts
create mode 100644 PWLukTS/tests/social.test.spec.ts
create mode 100644 PWLukTS/tests/test-1.spec.ts
create mode 100644 PWLukTS/tests/tittle.spec.ts
create mode 100644 PWLukTS/tests/vsechny.spec.ts
create mode 100644 PW_ZiveTS/.gitignore
create mode 100644 PW_ZiveTS/.idea/PW_ZiveTS.iml
create mode 100644 PW_ZiveTS/.idea/misc.xml
create mode 100644 PW_ZiveTS/.idea/modules.xml
create mode 100644 PW_ZiveTS/.idea/vcs.xml
create mode 100644 PW_ZiveTS/.idea/workspace.xml
create mode 100644 PW_ZiveTS/e2e/example.spec.ts
create mode 100644 PW_ZiveTS/package-lock.json
create mode 100644 PW_ZiveTS/package.json
create mode 100644 PW_ZiveTS/playwright.config.ts
create mode 100644 PW_ZiveTS/test/aa.spec.ts
create mode 100644 PW_ZiveTS/test/aaa.spec.ts
create mode 100644 PW_ZiveTS/test/example.spec.ts
create mode 100644 PW_ZiveTS/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright základy na GitHub/.gitignore
create mode 100644 Playwright základy na GitHub/.idea/.gitignore
create mode 100644 Playwright základy na GitHub/.idea/Playwright základy na GitHub.iml
create mode 100644 Playwright základy na GitHub/.idea/misc.xml
create mode 100644 Playwright základy na GitHub/.idea/modules.xml
create mode 100644 Playwright základy na GitHub/.idea/vcs.xml
create mode 100644 Playwright základy na GitHub/11 Identifikácia elementov a akcie s nimi.md
create mode 100644 Playwright základy na GitHub/Async Await.md
create mode 100644 Playwright základy na GitHub/README.MD
create mode 100644 Playwright základy na GitHub/fixtures/basePages.ts
create mode 100644 Playwright základy na GitHub/package-lock.json
create mode 100644 Playwright základy na GitHub/package.json
create mode 100644 Playwright základy na GitHub/page-objects copy/HomePage.ts
create mode 100644 Playwright základy na GitHub/page-objects copy/LoginPage.ts
create mode 100644 Playwright základy na GitHub/page-objects/HomePage.ts
create mode 100644 Playwright základy na GitHub/page-objects/LoginPage.ts
create mode 100644 Playwright základy na GitHub/playwright.config.ts
create mode 100644 Playwright základy na GitHub/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright základy na GitHub/tests/alerts.spec.ts
create mode 100644 Playwright základy na GitHub/tests/assertions.spec.ts
create mode 100644 Playwright základy na GitHub/tests/elementState.spec.ts
create mode 100644 Playwright základy na GitHub/tests/example.spec copy.ts
create mode 100644 Playwright základy na GitHub/tests/example.spec.ts
create mode 100644 Playwright základy na GitHub/tests/home.spec.ts
create mode 100644 Playwright základy na GitHub/tests/login.spec.ts
create mode 100644 Playwright základy na GitHub/tests/screenshots.spec.ts
create mode 100644 Playwright základy na GitHub/tests/tabs.spec.ts
create mode 100644 Playwright základy na GitHub/tests/upload.spec.ts
create mode 100644 Playwright základy na GitHub/Úkoly.md
create mode 100644 Playwright základy na GitHub/Čo je Page Object Model (POM).md
create mode 100644 Playwright_GH_TS/...github/workflows/search_google.yml
create mode 100644 Playwright_GH_TS/...github/workflows/zive.yml
create mode 100644 Playwright_GH_TS/aaa/.gitignore
create mode 100644 Playwright_GH_TS/aaa/package-lock.json
create mode 100644 Playwright_GH_TS/aaa/package.json
create mode 100644 Playwright_GH_TS/aaa/playwright.config.ts
create mode 100644 Playwright_GH_TS/aaa/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright_GH_TS/aaa/tests/aa.spec.ts
create mode 100644 Playwright_GH_TS/aaa/tests/aaa.spec.ts
create mode 100644 Playwright_GH_TS/aaa/tests/example.spec.ts
create mode 100644 Playwright_GH_TS/lukan/.gitignore
create mode 100644 Playwright_GH_TS/lukan/e2e/example.spec.ts
create mode 100644 Playwright_GH_TS/lukan/package-lock.json
create mode 100644 Playwright_GH_TS/lukan/package.json
create mode 100644 Playwright_GH_TS/lukan/page-objects/HomePage.ts
create mode 100644 Playwright_GH_TS/lukan/page-objects/LoginPage.ts
create mode 100644 Playwright_GH_TS/lukan/playwright.config.ts
create mode 100644 Playwright_GH_TS/lukan/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright_GH_TS/lukan/tests/cookies.test.spec.ts
create mode 100644 Playwright_GH_TS/lukan/tests/example.spec.ts
create mode 100644 Playwright_GH_TS/lukan/tests/login.spec.ts
create mode 100644 Playwright_GH_TS/lukan/tests/menuButton.test.spec.ts
create mode 100644 Playwright_GH_TS/lukan/tests/search.spec.ts
create mode 100644 Playwright_GH_TS/lukan/tests/test-1.spec.ts
create mode 100644 Playwright_GH_TS/search_google/.gitignore
create mode 100644 Playwright_GH_TS/search_google/package-lock.json
create mode 100644 Playwright_GH_TS/search_google/package.json
create mode 100644 Playwright_GH_TS/search_google/playwright.config.ts
create mode 100644 Playwright_GH_TS/search_google/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright_GH_TS/search_google/tests/example.spec.ts
create mode 100644 Playwright_GH_TS/zive.ts/.gitignore
create mode 100644 Playwright_GH_TS/zive.ts/package-lock.json
create mode 100644 Playwright_GH_TS/zive.ts/package.json
create mode 100644 Playwright_GH_TS/zive.ts/playwright.config.ts
create mode 100644 Playwright_GH_TS/zive.ts/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright_GH_TS/zive.ts/tests/example.spec.ts
create mode 100644 Playwright_GH_TS/zive/.gitignore
create mode 100644 Playwright_GH_TS/zive/e2e/example.spec.ts
create mode 100644 Playwright_GH_TS/zive/package-lock.json
create mode 100644 Playwright_GH_TS/zive/package.json
create mode 100644 Playwright_GH_TS/zive/playwright.config.ts
create mode 100644 Playwright_GH_TS/zive/tests-examples/demo-todo-app.spec.ts
create mode 100644 Playwright_GH_TS/zive/tests/example.spec.ts
diff --git a/PWLukTS/.gitignore b/PWLukTS/.gitignore
new file mode 100644
index 0000000..75e854d
--- /dev/null
+++ b/PWLukTS/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/PWLukTS/.idea/.gitignore b/PWLukTS/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/PWLukTS/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/PWLukTS/.idea/PWLukTS.iml b/PWLukTS/.idea/PWLukTS.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/PWLukTS/.idea/PWLukTS.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PWLukTS/.idea/misc.xml b/PWLukTS/.idea/misc.xml
new file mode 100644
index 0000000..639900d
--- /dev/null
+++ b/PWLukTS/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PWLukTS/.idea/modules.xml b/PWLukTS/.idea/modules.xml
new file mode 100644
index 0000000..b46564a
--- /dev/null
+++ b/PWLukTS/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PWLukTS/.idea/vcs.xml b/PWLukTS/.idea/vcs.xml
new file mode 100644
index 0000000..62bd7a0
--- /dev/null
+++ b/PWLukTS/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PWLukTS/.vs/PWLukTS/FileContentIndex/d716de9a-2387-475d-b8ff-c8380967c4a3.vsidx b/PWLukTS/.vs/PWLukTS/FileContentIndex/d716de9a-2387-475d-b8ff-c8380967c4a3.vsidx
new file mode 100644
index 0000000000000000000000000000000000000000..c0fa9b2a3964c706913895c04858256e38feabd6
GIT binary patch
literal 56597
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
literal 0
HcmV?d00001
diff --git a/PWLukTS/.vs/PWLukTS/v17/.wsuo b/PWLukTS/.vs/PWLukTS/v17/.wsuo
new file mode 100644
index 0000000000000000000000000000000000000000..2929932dea3e052fa87ecd6f3b86c72e1be746d7
GIT binary patch
literal 14848
zcmeHNU2Ggz6~5~>fdU2El(Y?hh9pgBJ?`wk$4!IR`xB?ci5ojMB8y;lcI<4t_O5nz
zlUT7IkopD>JQS%?9}of|)Jh;ER8<0r2cB9Lid2DwR9X;HBm`2PctIlO`)2lf#^as!
zde^B<*t0rw|L?g!=bU@)x%>OKANtD=etzVy!Uw0ted5mg1LB}3KjevvPYSz?d3V;=
z*IC3kqJ8)uT7iqAf?r(>imX^hSQ9JU%8uc%IIywjV2QR+{q-Af{Orv?g&t(BLr&Ex
zP^^fn;w-4oBbF66+Vpv<@`ItO$#Q
zNa7m|C&fUBQ+WW`DSh_#9zOs%f6lv#aa6D#CeR&)Q!nUytc&?Sg3cD39exPme_HS>
zW%^?%WB5^otny)mj{-jq`~;9|_z>`6AlHEXej3QST#v)eIQt+x-b_2G{&zpDzy~S+
z-~86=uKbf}0FwVdNclelx_$Y#mw!Wd9zc?MfUjU&i={3BC266+5zm35EqF2!?0alSvHDDos0(Q$
zE9hse3;HRvqs}Hwii_fs7~C@7L0ADLtbhrc?cj+n=#Qhrqg1?rcJe3v543NB0RD%K
zv!~PZhxea4lA&X9j}P%fvYft^N+S1?fp*av+hg)
zeTOFZ+3|#=a28fED)L~dncHk?2RQ#i^g+n`g|{pyPTz#w|7pyZmIt|sR7m+E@(dt(
z{CTVm*QJE7wP7W=J&%AsZC-h2a!Z0^JWFJ$ca0})zSf&NF$}5X`Y(fW5!^_wU$RFb
z+cM<*y{D14EGSOdqi=_>3;Vxg{_F4m{@MR_{5NZJe%-c_3md%87DY(L6pSK@%(vK{kYlJ1)RQD
zaq=vHmP6ZJ<5}NbuLXJdP6q7v6waN{s*XR8cSYX$2tNs>!9#i2K$gt~7UHz~7QqdS
zpGPh^#6T|jzlgju_MS{2RX}RiZaIk4s*E-(uoZY(EoLG
zad$JI8on&P-d$s~;{1C7+M>=|kXI5eA(uT*s5oG}KV>7;KIU!1_+10ueI}tx9m`-E
znv%DfI&!MbTm4nkcozOXdiFHY1OT~QK>AJOlz2_VXJBy!*y$Q^`@GO#Nx5>u#$?$
z6G}{tWRz4Ag9_&|*=BGTE%U_MPrWZ&k{Tda1HpGs}9tbj=*?eKcTj+jY*(DUJ14
zcK25Hev#=f=k6`%BdugEbonj{^pRlc+AF$4x*198iGr$_(O6uGMMg#xEdd1?H}(0b
z7BkIgLK{}qXe1n6_3PWmM5vWPg=*u<^o*lXqq>>Y)M!{S!vzBxwWum1^T`otR9!W+
za9CC2$u+ln*hs_*ni0Wp;t>u9nme8-DynXnVNES)nx61fhZ>J-u|h&oji{!?Vlk+9
zJu;$%RrI49NpnP-U*i!hAEx|M2uSa}-Q&Hz@a>MTnU1%R_ZH`73$7&waVMn35QcTYZ#&?2zp@XkSGiNp{Ws_i>R5%Z
z@3ZlC%60Co-`$a&yub9{f82GGv0mSQWIw%xuD_!*|BsiJ-~Ug4Am2V{!89bpj?q^q-~U?Cr!4Z_iSHiUji;OU5B~o+h(7(Fq)opd{eN7GcEI`X
zbp5$ryz>zJ^LOd5z$aS3D9TR7fG_#)VU+RzxSg)Q_x&ktd+jXv*JkZg>pFBBJ)q7W
z(C^kB`2QFBvw4s2hQ5FPbGX0K+epv54t76G4jfHeZ`Od=CxmT4tul{G`|++X>E+5oX?f#zep77%Y*|(HePr3%
zsyd{u=Jc{gPgD*F3Cl94w`RVg79#22@qqA>)|8FN=sy)Y9N}iTstMjOv`R}s+-!#t6yVE+g
z%lbdmi~heC-v6je_wfDCet(oa+Iq(~=kBNXKRL2)^><(Y@7P(qtNQ=7P;VZP{{l>5
BdR+hj
literal 0
HcmV?d00001
diff --git a/PWLukTS/.vs/VSWorkspaceState.json b/PWLukTS/.vs/VSWorkspaceState.json
new file mode 100644
index 0000000..0745879
--- /dev/null
+++ b/PWLukTS/.vs/VSWorkspaceState.json
@@ -0,0 +1,8 @@
+{
+ "ExpandedNodes": [
+ "",
+ "\\tests"
+ ],
+ "SelectedNode": "\\tests\\cookies.test.spec.ts",
+ "PreviewInSolutionExplorer": false
+}
\ No newline at end of file
diff --git a/PWLukTS/.vs/slnx.sqlite b/PWLukTS/.vs/slnx.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..96a7ac4ee9dd760a78d806066d12adb5ed9cd717
GIT binary patch
literal 102400
zcmeI53v3(7dH8q5mne!ml4XgKs0T&+&f?iCo06!*n&!tE174C
z)RC0$)a26AZVt@@*R<#h4f;5s?G?=xZ5kwPa0xC&k)&vgrtb&RrU=@imjLPg(f->e
z|H~igH?!~MlAqmxxR+#iv%{p}B>b_`5?lYqGKCZwkTZ%0hYiwb0;AbGB=dQoG+1m}E21S9cSc^k!O0
z*%>fj4^{6H)DoSST4FVJDw&iDh4e-`LyM?kwI;LF!LoFEk&OUpLs{M{6s2tqp;bgt
zhIH+Nu$<2&6SRFU<({it05#EcF0;FxEl=lfij+?KJb`s+SL>`@F;A0;Y*xxoCz7|N
z^>WwcR%T7uY;I-N=!$j>ZO*C#b+0`4waER|Cf4F!Tw^WHs%8D|Gq*TvG~}sqcNuz8=&xu^6uq9n=?H(1u$ri5OYN=GKo!W2H7S>Mbz$!6
zs`ksHrqPrP)k}?23I@M1szi3#(vfJf*Q9&_8d_P;e~)YQ1R@ds{#jNJRb#Sw^Ea=#
z4w@qkHBgN(UZfgoo?J6Dhvnkv-Rzltds#JKt*Esq(@XZG#}kN)>Ls&QK9dhD&KpMsFDGew|Oj&lz*UszB><>Ac$$xC1@kp5F5rTA`A4xu&82
zGbHyYvO=Xs8(C~{2Tq>kZ?KwEcoy!_r1gUI?5>oByEgMzhpFCJ
zA_ucj`A>mnq%_NW0>L1EPhvI|-<1CvOg3&RQbUzfByU?yyOzl8!gaLN&Q*9;Yi;sF
z=xSc#$Q!~7f+U=WKkas1A0bE7N>LB
z&Ggo8o((Q9!vkYFDZPh2n1+=E=XcYYlqppq{AB2+{Orn7(x>0>j%*I1!aJG7-gEi%
z*6rd@l35=r7Q)Japq_Fik>8Sv_UWH7rl(JJG}(-_vAY$P^L;@vSC9O&Fy)|o_rSRo
zVSKZ2>z4F9402|qp~8-o+^4*D8iy;fokhwVszld-)5%S@GI%ob!YYglD9^ZsOVZxW
zwGM}qJ5A>3OdniyI#n{A+sziE_ii`1xU=tnjE*L-Sb279S)MN_oRKzClPPIb93D1R7}KDV`6gS%=pCk9briSf~m&BSnOG$oF2jBKQ0sS$BvWNa9+8&8Q7
zlZjDrd~|qoSQ;LVjU|W0gftwDrjju+DGi?yN295PI2lcx0ZXH!iSe-*WV0zI$A%}y
z#*;~LY&>qu^6a5+}yS#>DZl
zs5G1cXU;?=F*P|H9Ztql;^cTN0hT1NG#(ofhc{yrV;h?rn`g$s(q?izHW{51CsGM;
zd}A~zZlp$IVstE)8s125j%|)bJ9sB|BCrfIFxv@bR5m&^G8FTZ_i=7=jU#_fzD}-@
zUnS3wkJQ-EF&Pp-0!RP}AOR$R1dsp{Kmter2_S(-oWK!>Bf`T_2U{vDFW_o8;sTkz
z+GHkCOpK7%xg%aCQI!ty_~oDd!Y4mC`0l;|H+K?J-7QGT+u1!eUQpwELvAht@v!|*QD4BWNB1ky
zu+CkKmT8Ck+}sFg7jnsTA~Qr+qL-!V?UhXwq(nY>yDa)xkDH5uiJg3UJCWZT+Lp4r
zWr-dSy1BEEh(?~@Ef#ZG`@DL(-P{RCm&t9Vvt?F-U2g6%h`lXkG*DVe`#NP%mZkh!
z{IP)SXEK+&la>nh`3F1X9Oh~Mi-|1>UIy^0sJq+U+%d@hVs2Zq#&sNZb3ur^n=A}H
zznyVvI&HGf;`RC6J1fgWX=B_GIc__b+RcDRM!i6*o9hL8+v!5m6w%e<<^~{QXbU#{
z+REngl9pEwfeXboy4m{#qQaq|deSDixquq03cLMKUPUP8rNVBes3`=R-CRHCb;D+1
zGneB-+bLBk>;sYzVfUtpnx?hdCiJ4sTjwwO6TQ;1{rKEKQt*YGV0(hWID-i4x0~c3~X>-bS
zhG6@xgevTG$jNoqYRr)aH`fluqcN(im3MP(RCZUhO6UK>{#Q8if5=yfi~J4wmhcn)
zSA_Qo+vE>~&j>e#j|oo+-xS^;ze8Szv4I~VX|gChF8nPyFSPmJ^1nf%FqZ>zQoMA+xjea+bVjO99^z!P+MXTT#;Pofy
z7L`>&8=)XxM}TL876SEx{-usZ`2CK4UK@aDW~6NPr|{9a`m&*hCTh(%zTlMwhCv0U
zCyz4dBMBaw%s71DrbW=l3|!P-Hc;R+m(9`WXlF%ja68((Fb=?;|H%wTzD@p{{5z}y
zc!T^C`3kHB_)GF7@`vQ}~Tn?TmVHyepEDW)*pM`xa>}6pO
z3xh1|W?>f#J6RZ@p|^vD?KE^AWnmi&Vfz4(w6d_p>3{?EXduwQPlIL}_-N2X11}94
zY2cxOn+7f#IBDRZL4(7=dFA>4JV*YGd`0-bumiw1g@QjXtO-w(--osTW5P>9r|>Vr
zUyzTJpCdm??viy9_y2$4_sE$44R{ye)8shmA};?g3!m`6^gVouv1mvD2_OL^fCP{L
z5(8rRS
zXZ3~NZfA_Qo!1w7dYxzaN;CUHu+4dbH_z=0-A9~{@ut~*p^FvAGQTf$vRv(E_=Nzo
zVVvXlcC|h
zn=``6)BF02f0Hwy$b?qsWAdNcoWJ1g=M0nnUCtP1nB3P-rSNq)Pa9*DHw>DXMbo^$
zA~8(-%c^BF|16Yq3VIOE(u^IV?%XHojf28Y+#&dJmN9%maz=l|*Z{|)4)Ir2vk
z;0FmH0VIF~kN^@u0!RP}AOR$R1dsp{cpC|fG&s4{W3`+R7#)fY#o+wIiJ|BS9Pzh$
ztd>I)4N7$Wzk&P=NB$TB{2&1&fCP{L5U6#(nEZ362RAlzZJeHd`kEg
z;U|UrLS9%GE(>Rch!7B5{{QfQ)&C{`XTb=5kN^@u0!RP}AOR$R1dsp{Kmtgh9t2w8
zGzQ+wyTc81Kle|$-uJl{IC+6e`k3UC-*#MYfwLBT32v9+XCV-OzsKCbrANx+A1}#7`tRbR08J%P)R_Sz^MRK;?gB>8UU4a=n^;z
zfJ)kR2^;}HC7p`o7k{_~4gjDMpC*y_|BG?3>)+?$ss9dni~JdRlYD}F4c-X&2l8q1
zpX4{-RDjpXD?}ncMsC0<08`|nunWLXk+b9jWR~0~m&tQ*V&DrTR*y=KHjn@kKmter
z2_OL^fCP{L51NN00y#Kmter
z2_OL^fCP{L5{
z;Pd}G=whHNNB{{S0VIF~kN^@u0!RP}AOR$R1RfFry8ge_caI}qfVcS13U3O(AUrRO
z`M>FZ)t~W)oBy==Bh8DxfA#$eB*707Kmter2_OL^fCP{L5UCU$mAmiI{`XY~C7%V;VV;6p}Ni|Jx9Bbn^o
zGRqWwzoy@B(l?xRua=a?i&534tiRaI^p|G?e9)-BEoFDl?-q->><|^JN^+74k+Q{$
zK4$UKQvtruXt9t>rV|+(Yc>%|*_yPn^_nb6iG1?5*`ChI@#W%`$=0(_5Aeb
zPUQE7Xfe!I>|$hF-`B`&Jv9>GyN$Loxvg~8Y{Vpzwcq7o+O$G;8nvb86Wcr1%WM(K
z7LL1_g@J(p@478zcBFg(YOwy7oa%~;rMdy^J9ZLVl9=0gMoL2cH+`9>J}*KvSg05w
z8+_Wy3{r#vqvOe3?oL`N*j!D!7}?g74rc3IG{AQmZP7xQ2^uA`?&Suid;MI1Z#TJY
z`f@S1E!maJAeYTe@XTBcTAgnvlh}JMpWeD%97^W0o9Qh|#YQG;(&zsskAov)!aoaz
z=3i@W@m*?q$@`(kFMAyBtBxOU_*TPG!_$ZE0EYQ{xiFEA)S%tQtNZ{7tflAOfSW!R^p+#g_-!fLpQC_wl>!MO(FPPStw7x
z78<;1&UP(QYWI5r)8OW;y_*Y(ZOPs_VISvo&3#;=3D-i?SLWwwSr@KMPc6qI??2b<
z35X*9L9v+F$Vk?rTfh4iXI7@p&&O>_?r$2Dxs*YiORWLlmH2FYDYUpWcX?{*M(9%f
zMrdkv<;vUwn7tfdSOKr#-|GDQDaFcSK6f{rlJZ7vHI1BvUWBFih4@l@VLHAXx~Xc!
zv+#Srw>GE=z(hn>f$O%qI72}IhUux9xG8ETJ|EYkRA*#>3)iNWrY}w{4Gza*5zS!>
zS5@W0ZYHyLbvKboZ>FV`odNUpQ1vcBEzxs
z*$9v}l;u6RyW7?fT16CPNY_3H%lTX~LEGn2?zzeZP!mn(GP~Q^@^t>DNa?iC6Ih3K
zwa(fV^E8>rW~KaeB6(X{FLzyTW!8kv=2m8nu4u>5=BzqU_sVl$i`-vrVlD2)HP+&+
zTGsDAbBnV^L;h-uvql-)ObsngxuAAFm(?6oi(~ojUttbcU({x8ERs$-b&SPcnGW4X-U(uQV*)#k0vTD9sQEO4Am+VQ8
zClD9aOJ=Re>x+rJlr65)Zrd8yXnXyvsf-p3m(rSy-Z0qxI-i1{Gv?oiC>snonHwhck`^p7S(PV8hOs)
z2}FZ@sh>5IWvN(9XSZ0JR_?cDiPK!4%G0rG#BTRxg-VS!ve@7boIJ_jU^S=kEZn0>
z>jmlAT`3E9ZRW2IQ@yc74rZb9p90NDX_of{f-nN={
zEs@!U>u9N+@{c5Y{^wre$U^f=zW?%FX^eW_>;9s1-tjUB-}cXoEk`}=qS*MM>vz-A
zbM#()F;TqDegR>0#`<01d~Gg%J!FjwB?|tg>vJm?q1{ZcE-lYpiPZUh|;B%={p-K&yrk|GUy_`h;1*a;sn=nNeZ-*`{UAbJAtKM?q
ze&ou6{#Eu;|4uz$9I|oGoaDmt(1W{MW`eo@-uH%1+&U40{}zX|VnR8Lr7qsqb>;F|
zE+tL9sHd4*Sb(R{r{TG?j08=2VHqCl_0&)po2zur=nw65RP4~@_?y(LyFB#7DU06)
ztz!6_sySPWli+G4-^#BMRM3m?m~3BJLUYUPMz%EA<_X-4@TD7;yNa1aF;Vd;NasV7
zb(p(Sb7Vyuwm;L<9PMaz8!G0t$d5dKgx$b?;H2d@nkwlE^jo{Rk2FUbx=M4z!Fo*H
zhUTjN6RZ4S>6PmMKeLB?@>Xw_jipl`b4dcGD7`rt6!3Zo7qrcnoWd$=TRO3)c3rFHEuS|8Mb
zN4;)n(MKya7A=LnDck0v>A=;}{+1np2ZrI6{hXeWcBE`7kuC1ybld(;+nUq4;e;F%%p9Ywp42F8WY<#uf>)(+!~%@)$tRx+1Rx&`*yEt?BCEHc*xSq
zv%Wo%UYvsr0cyCF3ab0|zxyC!@{8UOu7$Mjr9$WN5PKVKgtcRerSU^ps%*a0
z7jB}So-MVFa|5y1#`t)0G%8Mx$H3K*ktDbp+Z1EcL^QS;jSa`fhUW_Ks&HaAQ(P`4
z^2Oa9Sdd8fy?75@-xsEDp@g4=|8prx*<CM<
z|16LU)=sk8t|{9g#o}xoEXqDt%dRxkuMj>q%LN|_;R@OBN4kRAY#-wj1LD246SvtE
z=4P*AD0Xl|GG80xrJf!IYio?_eJEJXx$Z~PoccnLm3;&tcGnKEd^?*P-HM%&1KW`i
zmYXhhcPRwP2-p2k2%0k851=XWcqf#bU+slHyRo4b-`E={u$|0P0Y$O*0TopPrM3>h
z!b6YPUaN`PyF@jU(BMuYc}LzBD3^^?Mx!dk+gUw`LVfD0y*`y~?sNU9VkUY}GjeSz
zGhFIyQ!0}`%5~Lp!D@#lXL-w*XJugJ{#`!8$~*<=0=1*7xVO?4-4o5bI>k*L)|~8W
z#f(aWEeg`!DK79(klN+FA6h$|bFhUQFc|>Eu38Y&ODXTF#qM!tVPiI?KJcLG7DFXB
zY|%#VX5KTvx#~+yvXJPD7~>Pxt
zQP6gsy=5s?Wtg^J&cX(KP=0z7?W=`k#a-}j!ft%bQ0(A_DjF{Zc?BbhalZOuw9B+I
zL`!P8?F$^I^Zz5lmpHb{{}u8AoC5GHdk^4Ecnjdqls5oA4eS3uN`4C74EQH_PvFPM
z2grSLhe%|d%#tZMHQ+nKw}k&7H^?%%EPO-wSK+IqmH3E@93#CXKw{)Hi3nd7{tB|e
z4-!BENB{{S0VIF~kN^@u0!RP}Ac1-ipljyCBP<+dVU&h_XIOZeg+ny#5m|VOg(qp)
z{{#yoEF7d^=mZNNXW?Ts95~LxV=N5QFgU=%5DWWh*xSd#UKaMyusg`YZWeaYu(6Yc
z0Ty=9u&bSgM_JfL!@v<1vi1J~w*KG6*8jWM`hOQ&|KGvZ|97zU{~c`oe>+?M-_F+m
zx3l&CooxMoCtLsD$=3h-*!q8;y#Al=|92dw?a2ml37^6F|AVh2kcX%&6Y*?l8R!3N
zT$@p0vRo!xAyOuc^Zz*iUt=XRO@Xu8gY*9nIE|0<|Aqx+bV5=+?uS(;od2(NEuP&p
zH$#Z?|K-O~;rxG*E?~s@|B7otO|#=T|6k8#wq~@3+59TAt;X}}3>rB9uPmp~wHa|1PZm-)L_0J>zpUebW11-aUxM4-!BE
zNB{{S0VIF~9vK4HN7&NPRah$-u4RoW8>_D6r1bX;D~4C=XSn9fOWzk&)}%&Pxo~~g
zq*|P~WqY7}<(pKCx~=y78ODyT{O_yzqTH%b26NMQ~u|N9;S
z^ILXV?*rOS<+8|l(06%yYNuQAsQIl^in*!7nv?lf%&0VYQUMI_^7K3uz;=1>N7znh
z_6f#>u3Yc09WgDx7&VPG-;5}RVh1-QW3@3}>KRmk!t0;?4+W?>*ZokMQ_r5zAch_Q
zV!Bw&NM?wSYY-1?NP$>xywvlU4l(p_AewUB7osUO{UKLRjS26_LyPj^De&TW7EWzb
zjyo~Dcg{xWVJMa!J@0@Vh6?YzRES+O9yXiTnUSxM;`qmudt{6qDmu7%g%
zta^~r>ss|JzHYUuFNZ9@I!<3TkFc8RaOG32RL$#<<&hist@0_Ws+oo>svWMx70p)l
zralNLZMpxfnweU3J$YSr-0e?g~}2Q;Ta<
zs7rZ`2CI5dp90%1DXN;UT5?;>x*)K%T-B%gurMuLtD2w6z@$-^vYRYc^`SmxE*fry
zs%EDa*{D#L5*rOx^`K4#HatO8%}y(@L7_ecHW;kxL468b{_s^bQ?=OTYIP}mx!J1T
z)CWP?ut(KARRU#NbpcRjwW=@mD8D`wXEzX4HG3IFbpv&vQayvJ9@YSeKBle7yMdYa
h5~F8)7T_uErV^_h=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.2.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
+ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.34.3",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.34.3.tgz",
+ "integrity": "sha512-2pWd6G7OHKemc5x1r1rp8aQcpvDh7goMBZlJv6Co5vCNLVcQJdhxRL09SGaY6HcyHH9aT4tiynZabMofVasBYw==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ }
+ }
+}
diff --git a/PWLukTS/package.json b/PWLukTS/package.json
new file mode 100644
index 0000000..9c8322e
--- /dev/null
+++ b/PWLukTS/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "lukan",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.34.3"
+ }
+}
diff --git a/PWLukTS/page-objects/HomePage.ts b/PWLukTS/page-objects/HomePage.ts
new file mode 100644
index 0000000..0e5ee9a
--- /dev/null
+++ b/PWLukTS/page-objects/HomePage.ts
@@ -0,0 +1,83 @@
+import { Locator, Page, expect
+} from "@playwright/test";
+export class HomePage{
+ page: Page;
+ cookiesButtonAccept: Locator;
+ cookiesButtonDecline: Locator;
+ menuButtonUvodniStrana: Locator;
+ menuButtonOMne: Locator;
+ menuButtonZasadyOchranyOsobnichUdaju: Locator;
+ menuButtonPodporovatele: Locator;
+ searchButton: Locator;
+ searchFieldInput: Locator;
+ // verifyTitleHomePage: Locator;
+ XsocialButton: Locator;
+
+ constructor(page: Page){
+ this.page = page;
+ this.cookiesButtonAccept = page.getByText('Accept');
+ this.cookiesButtonDecline = page.getByText('Decline');
+ this.menuButtonUvodniStrana = page.getByRole('link', { name: 'Úvodní stránka' });
+ this.menuButtonOMne = page.getByRole('link' , { name: 'O Mně' });
+ this.menuButtonZasadyOchranyOsobnichUdaju = page.getByRole('link' , { name: 'Zásady ochrany osobních údajů' });
+ this.menuButtonPodporovatele = page.getByRole('link' , { name: 'Podporovatelé' });
+ this.searchFieldInput = page.locator('//*[@id="search-7"]/form/label/input');
+ this.searchButton = page.locator('#search-7 > form > button > svg > use');
+ // this.verifyTitleHomePage = expect.toHaveTitle('Lukáš bloguje - Blog o všem možném i nemožném');
+ this.XsocialButton = page.locator('#block-54 > ul > li.wp-social-link.wp-social-link-twitter.wp-block-social-link > a > svg > path');
+
+ }
+
+ async gotoHome() {
+ await this.page.goto('https://www.lukan.cz/');
+ return this;
+ }
+
+ async clickCookiesButtonAccept() {
+ await this.cookiesButtonAccept.click();
+ return this;
+ }
+
+ async clickCookiesButtonDecline() {
+ await this.cookiesButtonDecline.click();
+ return this;
+ }
+
+ async clickUvodniStranaButton() {
+ await this.menuButtonUvodniStrana.click();
+ return this;
+ }
+
+ async clickOMneButton() {
+ await this.menuButtonOMne.click();
+ return this;
+ }
+
+ async clickZOOUButton() {
+ await this.menuButtonZasadyOchranyOsobnichUdaju.click();
+ return this;
+ }
+
+ async clickPodporovateleButton() {
+ await this.menuButtonPodporovatele.click();
+ return this;
+ }
+ async enterTextSearchFields() {
+ await this.searchFieldInput.fill('Test');
+ return this;
+ }
+
+ async clickSearchButton() {
+ await this.searchButton.click();
+ return this;
+ }
+
+ async clickXsocialButton() {
+ await this.XsocialButton.click();
+ return this;
+ }
+
+
+
+
+}
\ No newline at end of file
diff --git a/PWLukTS/page-objects/LoginPage.ts b/PWLukTS/page-objects/LoginPage.ts
new file mode 100644
index 0000000..304c184
--- /dev/null
+++ b/PWLukTS/page-objects/LoginPage.ts
@@ -0,0 +1,7 @@
+import { Locator, Page
+} from "@playwright/test";
+export class LoginPage{
+ page: Page;
+
+
+}
\ No newline at end of file
diff --git a/PWLukTS/playwright.config.ts b/PWLukTS/playwright.config.ts
new file mode 100644
index 0000000..54fc979
--- /dev/null
+++ b/PWLukTS/playwright.config.ts
@@ -0,0 +1,78 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ headless: false,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ /*
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+*/
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ..devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/PWLukTS/tests-examples/demo-todo-app.spec.ts b/PWLukTS/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/PWLukTS/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/PWLukTS/tests/cookies.test.spec.ts b/PWLukTS/tests/cookies.test.spec.ts
new file mode 100644
index 0000000..97a5d00
--- /dev/null
+++ b/PWLukTS/tests/cookies.test.spec.ts
@@ -0,0 +1,19 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+
+// test odsouhlasení cookies
+test('HomePage click Accept cookies', async ({ page }) => {
+
+ const homePage = new HomePage(page);
+ await (await homePage.gotoHome())
+ .clickCookiesButtonAccept();
+ });
+
+
+ // test zamítnutí cookies
+test('HomaPage click Decline cookies' , async ({page}) => {
+ const homePage = new HomePage(page);
+ await (await homePage.gotoHome())
+ .clickCookiesButtonDecline();
+});
\ No newline at end of file
diff --git a/PWLukTS/tests/example.spec.ts b/PWLukTS/tests/example.spec.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/PWLukTS/tests/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/PWLukTS/tests/help.spec.ts b/PWLukTS/tests/help.spec.ts
new file mode 100644
index 0000000..501f8e4
--- /dev/null
+++ b/PWLukTS/tests/help.spec.ts
@@ -0,0 +1,10 @@
+import { test, expect } from '@playwright/test';
+import { loadHomepage, cokieeAccept
+ } from "../helpers";
+
+ test('1' , async ({ page }) => {
+ await loadHomepage(page);
+ await cokieeAccept(page);
+
+
+ });
\ No newline at end of file
diff --git a/PWLukTS/tests/login.spec.ts b/PWLukTS/tests/login.spec.ts
new file mode 100644
index 0000000..bd0afac
--- /dev/null
+++ b/PWLukTS/tests/login.spec.ts
@@ -0,0 +1 @@
+// login deaktivován
\ No newline at end of file
diff --git a/PWLukTS/tests/menuButton.test.spec.ts b/PWLukTS/tests/menuButton.test.spec.ts
new file mode 100644
index 0000000..37ca867
--- /dev/null
+++ b/PWLukTS/tests/menuButton.test.spec.ts
@@ -0,0 +1,12 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+test('Click menu button with HomePage' , async ({ page }) => {
+ const homePage = new HomePage(page);
+ await (await (await (await (await (await homePage.gotoHome())
+ .clickCookiesButtonAccept())
+ .clickUvodniStranaButton())
+ .clickOMneButton())
+ .clickZOOUButton())
+ .clickPodporovateleButton();
+});
\ No newline at end of file
diff --git a/PWLukTS/tests/primary.menu.spec.ts b/PWLukTS/tests/primary.menu.spec.ts
new file mode 100644
index 0000000..6fe9b29
--- /dev/null
+++ b/PWLukTS/tests/primary.menu.spec.ts
@@ -0,0 +1,29 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+
+
+// test Textů v menu
+test('Otestování textů v menu', async ({ page }) => {
+
+ const verifyTextMenu = [
+ "Úvodní stránka",
+ "O mně",
+ "Zásady ochrany osobních údajů",
+ "Podporovatelé"
+ ];
+
+ const homepage = new HomePage(page);
+ await homepage.gotoHome();
+
+
+ const navLinks = page.locator('#menu-menu-1 li[id*=menu]');
+
+ for (const el of await navLinks.elementHandles()) {
+ console.log(await el.textContent());
+ }
+
+ expect(await navLinks.allTextContents()).toEqual(verifyTextMenu);
+
+
+ });
\ No newline at end of file
diff --git a/PWLukTS/tests/search.spec.ts b/PWLukTS/tests/search.spec.ts
new file mode 100644
index 0000000..0f781be
--- /dev/null
+++ b/PWLukTS/tests/search.spec.ts
@@ -0,0 +1,10 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+test('Search web' , async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.enterTextSearchFields();
+ await homePage.clickSearchButton();
+
+});
\ No newline at end of file
diff --git a/PWLukTS/tests/social.test.spec.ts b/PWLukTS/tests/social.test.spec.ts
new file mode 100644
index 0000000..09bd351
--- /dev/null
+++ b/PWLukTS/tests/social.test.spec.ts
@@ -0,0 +1,26 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+
+
+test('Sociální sítě X- kliknutí na Homepage' , async ({page}) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ // Klikne na ikonu Twitter a přesměrujeme se na web Twitter
+ await homePage.clickXsocialButton();
+ const expectedUrl = 'https://twitter.com/KankysCZ';
+ // Kontrola odkazu z prokliku že jsem na správné stránce, než nám Musk změní adresu
+ const currentUrl = await page.url();
+ if (currentUrl === expectedUrl) {
+ console.log('Super jsi na správné stránce!');
+ } else {
+ console.log(`Průser jsi nejsi na správné stránce. Jsi tu ${currentUrl}`);
+ }
+
+
+
+});
+
+test('Sociální sítě LinkId - kliknutí na Hompage', ({page}) => {
+ const homePage = new HomePage(page);
+})
\ No newline at end of file
diff --git a/PWLukTS/tests/test-1.spec.ts b/PWLukTS/tests/test-1.spec.ts
new file mode 100644
index 0000000..29fdff8
--- /dev/null
+++ b/PWLukTS/tests/test-1.spec.ts
@@ -0,0 +1,6 @@
+import { test, expect } from '@playwright/test';
+
+test('test', async ({ page }) => {
+ await page.goto('https://lukan.cz/');
+ await page.getByRole('link', { name: 'Úvodní stránka' }).click();
+});
\ No newline at end of file
diff --git a/PWLukTS/tests/tittle.spec.ts b/PWLukTS/tests/tittle.spec.ts
new file mode 100644
index 0000000..7f14f65
--- /dev/null
+++ b/PWLukTS/tests/tittle.spec.ts
@@ -0,0 +1,9 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+test('Kontrola titulku', async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await expect(page).toHaveTitle('Lukáš bloguje - Blog o všem možném i nemožném');
+
+});
\ No newline at end of file
diff --git a/PWLukTS/tests/vsechny.spec.ts b/PWLukTS/tests/vsechny.spec.ts
new file mode 100644
index 0000000..7353c4b
--- /dev/null
+++ b/PWLukTS/tests/vsechny.spec.ts
@@ -0,0 +1,89 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+test.describe('Kolekce testů', () => {
+ test('Kontrola titulku', async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await expect(page).toHaveTitle('Lukáš bloguje - Blog o všem možném i nemožném');
+
+ });
+
+ test('Kontrola textu na stránce', async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+
+ const headingText = await page.locator('text=Lukáš bloguje Blog o všem možném i nemožném ');
+ await expect(headingText).toBeVisible();
+
+ await expect(page).toHaveURL(/./);
+
+ });
+
+ test('HomePage click Accept cookies', async ({ page }) => {
+
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.clickCookiesButtonAccept();
+ });
+
+
+ // test zamítnutí cookies
+ test('HomaPage click Decline cookies' , async ({page}) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.clickCookiesButtonDecline();
+ })
+
+ test('Click menu button with HomePage' , async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.clickUvodniStranaButton();
+ await homePage.clickOMneButton();
+ await homePage.clickZOOUButton();
+ await homePage.clickPodporovateleButton();
+ });
+
+ test('Search web' , async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.enterTextSearchFields();
+ await homePage.clickSearchButton();
+
+ });
+
+ test('test', async ({ page }) => {
+ await page.goto('https://lukan.cz/');
+ await page.getByRole('link', { name: 'Úvodní stránka' }).click();
+ });
+
+ test('Otestování textů v menu', async ({ page }) => {
+
+ const verifyTextMenu = [
+ "Úvodní stránka",
+ "O mně",
+ "Zásady ochrany osobních údajů",
+ "Podporovatelé"
+ ];
+
+ await page.goto('https://lukan.cz/')
+
+
+ const navLinks = page.locator('#menu-menu-1 li[id*=menu]');
+
+ for (const el of await navLinks.elementHandles()) {
+ console.log(await el.textContent());
+ }
+
+ expect(await navLinks.allTextContents()).toEqual(verifyTextMenu);
+
+
+ });
+
+
+
+
+
+
+});
+
diff --git a/PW_ZiveTS/.gitignore b/PW_ZiveTS/.gitignore
new file mode 100644
index 0000000..706b4ea
--- /dev/null
+++ b/PW_ZiveTS/.gitignore
@@ -0,0 +1,16 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/PW_ZiveTS/.idea/PW_ZiveTS.iml b/PW_ZiveTS/.idea/PW_ZiveTS.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/PW_ZiveTS/.idea/PW_ZiveTS.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PW_ZiveTS/.idea/misc.xml b/PW_ZiveTS/.idea/misc.xml
new file mode 100644
index 0000000..639900d
--- /dev/null
+++ b/PW_ZiveTS/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PW_ZiveTS/.idea/modules.xml b/PW_ZiveTS/.idea/modules.xml
new file mode 100644
index 0000000..2b302f1
--- /dev/null
+++ b/PW_ZiveTS/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PW_ZiveTS/.idea/vcs.xml b/PW_ZiveTS/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/PW_ZiveTS/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PW_ZiveTS/.idea/workspace.xml b/PW_ZiveTS/.idea/workspace.xml
new file mode 100644
index 0000000..6689e56
--- /dev/null
+++ b/PW_ZiveTS/.idea/workspace.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1692115830712
+
+
+ 1692115830712
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/test/aaa.spec.ts
+ 5
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PW_ZiveTS/e2e/example.spec.ts b/PW_ZiveTS/e2e/example.spec.ts
new file mode 100644
index 0000000..54a906a
--- /dev/null
+++ b/PW_ZiveTS/e2e/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects page to have a heading with the name of Installation.
+ await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
+});
diff --git a/PW_ZiveTS/package-lock.json b/PW_ZiveTS/package-lock.json
new file mode 100644
index 0000000..5cc717c
--- /dev/null
+++ b/PW_ZiveTS/package-lock.json
@@ -0,0 +1,67 @@
+{
+ "name": "zive",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "zive",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.37.0"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.37.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.0.tgz",
+ "integrity": "sha512-181WBLk4SRUyH1Q96VZl7BP6HcK0b7lbdeKisn3N/vnjitk+9HbdlFz/L5fey05vxaAhldIDnzo8KUoy8S3mmQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.37.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.15.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
+ "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.37.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.0.tgz",
+ "integrity": "sha512-1c46jhTH/myQw6sesrcuHVtLoSNfJv8Pfy9t3rs6subY7kARv0HRw5PpyfPYPpPtQvBOmgbE6K+qgYUpj81LAA==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ }
+ }
+}
diff --git a/PW_ZiveTS/package.json b/PW_ZiveTS/package.json
new file mode 100644
index 0000000..48f1c5b
--- /dev/null
+++ b/PW_ZiveTS/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "zive",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.37.0"
+ }
+}
diff --git a/PW_ZiveTS/playwright.config.ts b/PW_ZiveTS/playwright.config.ts
new file mode 100644
index 0000000..f389f33
--- /dev/null
+++ b/PW_ZiveTS/playwright.config.ts
@@ -0,0 +1,90 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './test',
+ /* Maximum time one test can run for. */
+ timeout: 30 * 1000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 5000
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 0,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+/*
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+*/
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { channel: 'chrome' },
+ // },
+ ],
+
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ // outputDir: 'test-results/',
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // port: 3000,
+ // },
+});
diff --git a/PW_ZiveTS/test/aa.spec.ts b/PW_ZiveTS/test/aa.spec.ts
new file mode 100644
index 0000000..66af5d1
--- /dev/null
+++ b/PW_ZiveTS/test/aa.spec.ts
@@ -0,0 +1,26 @@
+const { chrome } = require('playwright');
+
+(async () => {
+ const browser = await chrome.launch({
+ headless: false
+ });
+ const context = await browser.newContext();
+ const page = await context.newPage();
+ await page.goto('https://www.aaaauto.cz/');
+ await page.getByRole('button', { name: 'Přijmout vše' }).click();
+ await page.getByRole('button', { name: 'Značka Vyberte značku' }).click();
+ await page.getByText('Škoda (2793)').first().click();
+ await page.getByRole('button', { name: 'Model Vyberte model' }).click();
+ await page.getByText('Citigo(40)').click();
+ await page.getByRole('button', { name: 'Rok Vyberte stáří vozu' }).click();
+ await page.getByText('Do 10 let').click();
+ await page.getByRole('button', { name: 'Cena Vyberte cenu' }).click();
+ await page.getByText('Do 200 000 Kč', { exact: true }).click();
+ await page.getByRole('button', { name: 'Kategorie Vyberte kategorii' }).click();
+ await page.getByText('Úsporné vozy (31)').click();
+ await page.getByRole('button', { name: 'Hledat' }).click();
+
+ // ---------------------
+ await context.close();
+ await browser.close();
+})();
\ No newline at end of file
diff --git a/PW_ZiveTS/test/aaa.spec.ts b/PW_ZiveTS/test/aaa.spec.ts
new file mode 100644
index 0000000..0ac2afc
--- /dev/null
+++ b/PW_ZiveTS/test/aaa.spec.ts
@@ -0,0 +1,17 @@
+import { test, expect } from '@playwright/test';
+
+test('test', async ({ page }) => {
+ await page.goto('https://www.aaaauto.cz/');
+ await page.getByRole('button', { name: 'Přijmout vše' }).click();
+ await page.getByRole('button', { name: 'Značka Vyberte značku' }).click();
+ await page.getByText('Škoda', { exact: true }).first().click();
+ await page.getByRole('button', { name: 'Model Vyberte model' }).click();
+ await page.getByText('Citigo(40)').click();
+ await page.getByRole('button', { name: 'Rok Vyberte stáří vozu' }).click();
+ await page.getByText('Do 10 let').click();
+ await page.getByRole('button', { name: 'Cena Vyberte cenu' }).click();
+ await page.getByText('Do 200 000 Kč', { exact: true }).click();
+ await page.getByRole('button', { name: 'Kategorie Vyberte kategorii' }).click();
+ await page.locator('#hpFilterNG').getByText('Úsporné vozy').click();
+ await page.getByRole('button', { name: 'Hledat' }).click();
+});
\ No newline at end of file
diff --git a/PW_ZiveTS/test/example.spec.ts b/PW_ZiveTS/test/example.spec.ts
new file mode 100644
index 0000000..3914e7f
--- /dev/null
+++ b/PW_ZiveTS/test/example.spec.ts
@@ -0,0 +1,44 @@
+import { test, expect } from '@playwright/test';
+import { chromium } from '@playwright/test';
+
+
+ test('has title', async ({ page }) => {
+
+ await page.goto('https://zive.cz');
+
+ // Odsouhlasí cookies
+ await page.click("//button[@id='didomi-notice-agree-button']/span");
+
+ // takto
+ //const button = await page.locator("//button[@id='didomi-notice-agree-button']");
+ //await button.click();
+
+ // Klikne na menu
+ await page.click("//a[contains(text(),'Menu')]");
+
+ // vrátí se na hlavní stranu
+ await page.click("#mainFORM > nav > div > div.header > a.mn-logo");
+
+ // klikne do vyhledávání
+ await page.click("//a[@onclick='layout.menu.toggle(true)']");
+
+ // klikne do vyhledávacího pole
+ const searchInput = await page.$("#mainFORM > nav > div > div.header > div");
+ await searchInput?.type('test');
+
+ // Potvrdíme vyhledávání stisknutím klávesy Enter
+ await searchInput?.press('Enter');
+
+ // vrátíme se na hlavní stránku
+ await page.click('//*[@id="mainFORM"]/div[3]/header/div[2]/div/a');
+
+ // Zkontrolujeme, že stránka má správný název
+ const pageTitle = await page.title();
+ expect(pageTitle).toBe('Živě.cz – O počítačích, internetu, vědě a technice');
+
+
+
+
+
+
+ });
diff --git a/PW_ZiveTS/tests-examples/demo-todo-app.spec.ts b/PW_ZiveTS/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/PW_ZiveTS/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright základy na GitHub/.gitignore b/Playwright základy na GitHub/.gitignore
new file mode 100644
index 0000000..75e854d
--- /dev/null
+++ b/Playwright základy na GitHub/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/Playwright základy na GitHub/.idea/.gitignore b/Playwright základy na GitHub/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/Playwright základy na GitHub/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/Playwright základy na GitHub/.idea/Playwright základy na GitHub.iml b/Playwright základy na GitHub/.idea/Playwright základy na GitHub.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/Playwright základy na GitHub/.idea/Playwright základy na GitHub.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Playwright základy na GitHub/.idea/misc.xml b/Playwright základy na GitHub/.idea/misc.xml
new file mode 100644
index 0000000..639900d
--- /dev/null
+++ b/Playwright základy na GitHub/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Playwright základy na GitHub/.idea/modules.xml b/Playwright základy na GitHub/.idea/modules.xml
new file mode 100644
index 0000000..57ecd1d
--- /dev/null
+++ b/Playwright základy na GitHub/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Playwright základy na GitHub/.idea/vcs.xml b/Playwright základy na GitHub/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/Playwright základy na GitHub/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Playwright základy na GitHub/11 Identifikácia elementov a akcie s nimi.md b/Playwright základy na GitHub/11 Identifikácia elementov a akcie s nimi.md
new file mode 100644
index 0000000..b6c7035
--- /dev/null
+++ b/Playwright základy na GitHub/11 Identifikácia elementov a akcie s nimi.md
@@ -0,0 +1,7 @@
+O tom ako písať Xpathy a využívať ich v testovaní sa dočítaš napríklad tu: https://www.guru99.com/xpath-selenium.html alebo tu.
+
+Viac o lokátoroch v Playwrighte nájdeš v dokumentácii tu.https://playwright.dev/docs/locators
+
+Taktiež dobrý blog o lokalizovaní elementov v napísala ambasádorka Playwrightu, Debbie: https://debbie.codes/blog/how-to-locate-elements-in-playwright/
+
+Skvelým rozšírením do Chrome prehliadača je [Selectors Hub](https://chrome.google.com/webstore/detail/selectorshub/ndgimibanhlabgdgjcpbbndiehljcpfh). Ako ho využívať uvidíš v [tomto](https://youtu.be/-GpTpwOLCUM) videu.
\ No newline at end of file
diff --git a/Playwright základy na GitHub/Async Await.md b/Playwright základy na GitHub/Async Await.md
new file mode 100644
index 0000000..fe27756
--- /dev/null
+++ b/Playwright základy na GitHub/Async Await.md
@@ -0,0 +1,158 @@
+[Portfolio](https://www.kutac.cz#portfolio) [Blog](https://www.kutac.cz/blog) [Kontakt](https://www.kutac.cz#kontakt)
+
+[Blog](https://www.kutac.cz/blog) [Weby a vše okolo](https://www.kutac.cz/weby-a-vse-okolo) Async / await
+
+# Async / await
+
+[Kategorie](#)
+
+[Co na srdci, to na blogu](https://www.kutac.cz/co-na-srdci-to-na-blogu) [Weby a vše okolo](https://www.kutac.cz/weby-a-vse-okolo) [Počítače a internety](https://www.kutac.cz/pocitace-a-internety) [Erasmus a cestování](https://www.kutac.cz/erasmus-a-cestovani)
+
+[Tagy](#)
+
+[PHP](https://www.kutac.cz/php) [JavaScript](https://www.kutac.cz/javascript) [Laravel](https://www.kutac.cz/laravel) [Go](https://www.kutac.cz/golang) [Git](https://www.kutac.cz/git) [Kvalita kódu](https://www.kutac.cz/kvalita-kodu) [Bezpečnost](https://www.kutac.cz/bezpecnost) [Databáze](https://www.kutac.cz/databaze) [Windows](https://www.kutac.cz/windows) [Linux](https://www.kutac.cz/linux) [Google](https://www.kutac.cz/google) [HTML](https://www.kutac.cz/html) [CSS](https://www.kutac.cz/css) [htaccess](https://www.kutac.cz/htaccess) [Tipy & triky](https://www.kutac.cz/tiky-a-triky) [O mně](https://www.kutac.cz/o-mne)
+
+[Rychlé odkazy](#)
+
+[Seriály](https://www.kutac.cz/serialy) [Testovací data k článkům](https://testdata.kutac.cz/) [Čtenářský deník](https://www.kutac.cz/co-na-srdci-to-na-blogu/ctenarsky-denicek)
+
+25.04.2018 22:52 Michael 1
+
+[JavaScript](https://www.kutac.cz/javascript)
+
+Asynchronní programování v Javascriptu bez callbacků a Promise nemusí být nutně sci-fi. Co všechno umí klíčová slova async / await?
+
+[![Async / await v Javascriptu](:/6ce0f100ec5a4fefb8cd737380630c3e "Async / await v Javascriptu")](https://www.kutac.cz/uploads/main-41375.jpg)
+
+S příchodem [Promise](https://www.kutac.cz/weby-a-vse-okolo/promise-v-javascriptu) se změnil a podstatně zjednodušil způsob, jakým je možné pracovat s asynchroními voláními v Javascriptu. Špatně škálovatelné volání callback funkcí nahradilo zpracování, které je přehledné, řetězitelné a s jednoduchým zachytáváním chyb. Od uvedení [generátorů](https://www.kutac.cz/weby-a-vse-okolo/generatory-v-javascriptu) je také možné pomocí pozastavitelných funkcí zpracovávat Promise způsobem, který vypadá synchronně, ale na pozadí není (viz [příklad](https://www.kutac.cz/weby-a-vse-okolo/generatory-v-javascriptu#vyuziti-a-zaver) ve [článku o generátorech](https://www.kutac.cz/weby-a-vse-okolo/generatory-v-javascriptu)). Takový zápis může být mnohem čitelnější a čitelnější kód znamená snazší debugování. Až by mohlo někoho napadnout, proč něco takového není standardní součástí jazyka. A tak nám do Javascriptu přibyly slova async a await.
+
+## [](#async-await)Async / Await
+
+Klíčové slovo async je označením funkce, která je pozastavitelná, podobně jako generátory. V takto označené funkci pak lze použít klíčové slovo await. To automaticky vyřeší a přiřadí výsledek Promise do dané proměnné. Vezměme tento příklad, kdy na základě dat článku vypíšeme jméno jeho autora:
+
+```javascript
+// Požadavek pomocí Promise
+function printAuthor(postId) {
+ fetch(`api/posts/${postId}`)
+ .then(res => res.json())
+ .then(post => fetch(`api/users/${post.userId}`))
+ .then(res => res.json())
+ .then(user => console.log(user.name}))
+ .catch(error => console.log(error))
+}
+
+printAuthor(42) // 'Douglas Adams'
+```
+
+Stejný příklad by při použití async / await vypadal takto:
+
+```javascript
+// Požadavek pomocí async funkce
+async function printAuthorAsync(postId) {
+ let res = await fetch(`api/posts/${postId}`)
+ const post = await res.json()
+
+ res = await fetch(`api/users/${post.userId}`)
+ const user = await res.json()
+
+ console.log(user.name)
+}
+
+printAuthorAsync(42) // 'Douglas Adams'
+
+
+```
+
+Příklad si můžete [vyzkoušet zde](https://jsfiddle.net/xs3yvp2d/13/).
+
+Await prakticky říká "počkej, až se vyřeší tento Promise a pokračuj". To je důležitá vlastnost, protože await pracuje pouze s Promise. Pokud dostane cokoliv jiného, převede výsledek zase na Promise. Ten vždy vrací i samotná async funkce. Celý tento mechanismus je totiž postaven nad Promise a generátory. Async / await je ve skutečnosti jenom syntaktické pozlátko a využívá stávající funkce jazyka.
+
+## [](#vyhody)Výhody
+
+Proč jej vlastně používat? Tím, že se zapisuje v podstatě synchronně, je mnohem čitelnější a nastavení breakpointů při debugování je tak mnohem jednodušší.
+
+Protože každé zpracování Promise nemá odlišný scope, všechny hodnoty mohou být k dispozici v celém scopu funkce:
+
+```javascript
+// Všechny výsledky Promise mohou být k dispozici v jednom scopu
+async function foo() {
+ const a = await promise1()
+ const b = await promise2()
+
+ return {a, b}
+}
+
+
+```
+
+Taky je možné používat klasické podmínky mnohem snáze:
+
+```javascript
+// Funkci lze jednodušše větvit podmínkami
+async function foo() {
+ const a = await promise1()
+ const b = await promise2()
+
+ if (a > b) {
+ return a
+ }
+ else {
+ return b
+ }
+}
+```
+
+A zachytávání výjimkek probíhá pomocí try/catch bloků:
+
+```javascript
+// Chyby jsou zachytávány try/catch bloky
+async function foo() {
+ try {
+ const a = await promise1()
+ const b = await promise2()
+
+ return {a, b}
+ }
+ catch (error) {
+ console.error(error)
+ }
+}
+
+
+```
+
+## [](#zaver)Závěr
+
+Async / await je velmi vítaným přínosem do Javascriptu. Pro asynchronní programování nabízí mnohdy čitelnější alternativu dnes rozšířeného Promise. Je podporován v NodeJS verze > 7.10 a větší částí posledních verzí moderních [prohlížečů](https://caniuse.com/#search=await). Přesto, pokud jej hodláte použít v prohlížeči, doporučoval bych nejdříve [transformaci Babelem](https://babeljs.io/docs/plugins/transform-async-to-generator/).
+
+* * *
+
+Napadá vás kdy je lepší použít Promise a naopak? Máte s async / await nějaké zkušenosti? Podělte se s ostatními v komentářích.
+
+## Přidat komentář
+
+Tvoje jméno *
+
+
+
+Tvůj email
+
+Tvůj web
+
+Tvůj komentář *
+
+Položky označené * jsou povinné. Email nebude zveřejněn
+
+* * *
+
+## Komentáře
+
+27.03.2021 22:13
+
+**Milos Leng**
+
+Super stranka, vysvetlenia lepsie ako v anglickych videach, ale stali mi je to malo :D
+
+[Odpovědět](#comment-473)
+
+© 2014 - 2023 All rights reserved, IČO: 01827219
\ No newline at end of file
diff --git a/Playwright základy na GitHub/README.MD b/Playwright základy na GitHub/README.MD
new file mode 100644
index 0000000..a67c9cc
--- /dev/null
+++ b/Playwright základy na GitHub/README.MD
@@ -0,0 +1,44 @@
+Pozor TypeScript
+
+
+
+súboj Playwrightu a Cypressu si pozrieš na týchto odkazoch:
+https://youtu.be/fncL63KRA-0
+https://www.youtube.com/live/bvvTzHmLWwY?feature=share
+https://youtu.be/RwNZTjwhgXc
+
+
+
+Node.js si stiahneš na tomto odkaze: https://nodejs.org/en
+npm si inštalovať nemusíš. Node.js ho už obsahuje.
+
+Visual Studio Code si stiahneš tu: https://code.visualstudio.com/
+
+
+
+
+
+
+Viac o fixtures sa dozvieš v Playwright dokumentácii tu:
+https://playwright.dev/docs/api/class-fixtures
+
+Všetky spôsoby spúšťania testov nájdeš tu.https://playwright.dev/docs/running-tests
+
+
+
+Viac o async a await sa dočítaš tu: https://www.kutac.cz/weby-a-vse-okolo/async-await
+
+
+
+Viac o configu nájdeš v dokumentácii tu.https://playwright.dev/docs/test-configuration
+
+
+O tom ako písať Xpathy a využívať ich v testovaní sa dočítaš napríklad tu: https://www.guru99.com/xpath-selenium.html alebo tu.
+
+Viac o lokátoroch v Playwrighte nájdeš v dokumentácii tu.https://playwright.dev/docs/locators
+
+Taktiež dobrý blog o lokalizovaní elementov v napísala ambasádorka Playwrightu, Debbie: https://debbie.codes/blog/how-to-locate-elements-in-playwright/
+
+Skvelým rozšírením do Chrome prehliadača je [Selectors Hub](https://chrome.google.com/webstore/detail/selectorshub/ndgimibanhlabgdgjcpbbndiehljcpfh). Ako ho využívať uvidíš v [tomto](https://youtu.be/-GpTpwOLCUM) videu.
+
+
diff --git a/Playwright základy na GitHub/fixtures/basePages.ts b/Playwright základy na GitHub/fixtures/basePages.ts
new file mode 100644
index 0000000..e69de29
diff --git a/Playwright základy na GitHub/package-lock.json b/Playwright základy na GitHub/package-lock.json
new file mode 100644
index 0000000..ad04375
--- /dev/null
+++ b/Playwright základy na GitHub/package-lock.json
@@ -0,0 +1,63 @@
+{
+ "name": "Playwright základy na GitHub",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "@playwright/test": "^1.34.3"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.34.3",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.34.3.tgz",
+ "integrity": "sha512-zPLef6w9P6T/iT6XDYG3mvGOqOyb6eHaV9XtkunYs0+OzxBtrPAAaHotc0X+PJ00WPPnLfFBTl7mf45Mn8DBmw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.34.3"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.2.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
+ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.34.3",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.34.3.tgz",
+ "integrity": "sha512-2pWd6G7OHKemc5x1r1rp8aQcpvDh7goMBZlJv6Co5vCNLVcQJdhxRL09SGaY6HcyHH9aT4tiynZabMofVasBYw==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ }
+ }
+}
diff --git a/Playwright základy na GitHub/package.json b/Playwright základy na GitHub/package.json
new file mode 100644
index 0000000..824e4f4
--- /dev/null
+++ b/Playwright základy na GitHub/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "playwright_zaklady",
+ "scripts": {
+ "allTests": "playwright test"
+ },
+ "keywords": [],
+ "author": "Kankys",
+ "description": "",
+ "devDependencies": {
+ "@playwright/test": "^1.34.3"
+ }
+}
\ No newline at end of file
diff --git a/Playwright základy na GitHub/page-objects copy/HomePage.ts b/Playwright základy na GitHub/page-objects copy/HomePage.ts
new file mode 100644
index 0000000..b1e2494
--- /dev/null
+++ b/Playwright základy na GitHub/page-objects copy/HomePage.ts
@@ -0,0 +1,45 @@
+// import třída modulů které budeme potřebovat
+import { Locator, Page
+} from "@playwright/test";
+
+//definujeme třídy (class)
+export class HomePage{
+ page: Page;
+ menu: Locator;
+ title: Locator;
+ item: Locator;
+ addToCart: Locator;
+ cardBadge: Locator;
+
+ // vytvoříme konstruktor třídy definuje proměnou page
+ constructor(page: Page) {
+ this.page = page;
+ this.menu = page.locator('#react-burger-menu-btn');
+ this.title = page.getByText('Swag Labs');
+ this.item = page.locator('#item_4_title_link');
+ this.addToCart = page.locator('#add-to-cart-sauce-labs-backpack');
+ this.cardBadge = page.locator('//span[@class="shopping_cart_badge"]');
+ }
+
+ // teď si definujeme metody na práci s elementy výše
+
+ async clickOnMenu() {
+ await this.menu.click();
+ }
+
+ async clickOnItem() {
+ await this.item.click();
+ }
+
+
+ async clickOnAddToCart() {
+ await this.addToCart.click();
+ }
+
+ async clickOnCardBAdge() {
+ await this.cardBadge.click();
+}
+
+
+}
+
diff --git a/Playwright základy na GitHub/page-objects copy/LoginPage.ts b/Playwright základy na GitHub/page-objects copy/LoginPage.ts
new file mode 100644
index 0000000..8005fa6
--- /dev/null
+++ b/Playwright základy na GitHub/page-objects copy/LoginPage.ts
@@ -0,0 +1,71 @@
+// import třída modulů které budeme potřebovat
+import { Locator, Page
+ } from "@playwright/test";
+
+//definujeme třídy (class)
+export class LoginPage{
+ page: Page;
+ userNameInput: Locator;
+ passwordInput: Locator;
+ loginButton: Locator;
+ invalidCredentialsErrorMessage: Locator;
+ requiredCredentialsErrorMassage: Locator;
+ lockedOutErrorMassage: Locator;
+
+ // vytvoříme konstruktor třídy definuje proměnou page
+ constructor(page: Page) {
+ this.page = page;
+ this.userNameInput = page.locator('#user-name');
+ this.passwordInput = page.locator('#password');
+ this.loginButton = page.locator('#login-button');
+ this.invalidCredentialsErrorMessage = page.getByText('Epic sadface: Username and password do not match any user in this service');
+ this.requiredCredentialsErrorMassage = page.getByText('Epic sadface: Username is required');
+ this.lockedOutErrorMassage = page.getByText('')
+ this.lockedOutErrorMassage = page.getByText('Epic sadface: Sorry, this user has been locked out.');
+ }
+
+ // teď si definujeme metody na práci s elementy výše this.userNameInput = page.locator('#user-name');
+ // metoda na přejetí na stránku s Loginem
+ async gotoLoginPage() {
+ await this.page.goto('https://www.saucedemo.com/');
+ }
+
+ // metoda přihlašovacího jména
+ // protože chceme vepsat uživatelské jmáno použijeme metodu fill
+ async enterValidUsername() {
+ await this.userNameInput.fill('standard_user');
+ }
+
+ async enterLockedOutUser() {
+ await this.userNameInput.fill('locked_out_user');
+}
+
+ // alternativy k valid budou invalid
+ async enterInvalidUsername() {
+ await this.userNameInput.fill('jmeno');
+}
+
+ // zde zapíšeme heslo
+ async enterValidPassword() {
+ await this.passwordInput.fill('secret_sauce');
+ }
+
+ async enterInvalidPassword() {
+ await this.passwordInput.fill('heslo');
+ }
+
+ // klik na login button
+ async clickLoginButton() {
+ await this.loginButton.click();
+ }
+
+
+ // valid metody na ´ůspěšné přihlášení zapozdříme tímto způsobem:
+ async login() {
+ await this.userNameInput.fill('standard_user');
+ await this.passwordInput.fill('secret_sauce');
+ await this.loginButton.click();
+ }
+
+}
+
diff --git a/Playwright základy na GitHub/page-objects/HomePage.ts b/Playwright základy na GitHub/page-objects/HomePage.ts
new file mode 100644
index 0000000..b1e2494
--- /dev/null
+++ b/Playwright základy na GitHub/page-objects/HomePage.ts
@@ -0,0 +1,45 @@
+// import třída modulů které budeme potřebovat
+import { Locator, Page
+} from "@playwright/test";
+
+//definujeme třídy (class)
+export class HomePage{
+ page: Page;
+ menu: Locator;
+ title: Locator;
+ item: Locator;
+ addToCart: Locator;
+ cardBadge: Locator;
+
+ // vytvoříme konstruktor třídy definuje proměnou page
+ constructor(page: Page) {
+ this.page = page;
+ this.menu = page.locator('#react-burger-menu-btn');
+ this.title = page.getByText('Swag Labs');
+ this.item = page.locator('#item_4_title_link');
+ this.addToCart = page.locator('#add-to-cart-sauce-labs-backpack');
+ this.cardBadge = page.locator('//span[@class="shopping_cart_badge"]');
+ }
+
+ // teď si definujeme metody na práci s elementy výše
+
+ async clickOnMenu() {
+ await this.menu.click();
+ }
+
+ async clickOnItem() {
+ await this.item.click();
+ }
+
+
+ async clickOnAddToCart() {
+ await this.addToCart.click();
+ }
+
+ async clickOnCardBAdge() {
+ await this.cardBadge.click();
+}
+
+
+}
+
diff --git a/Playwright základy na GitHub/page-objects/LoginPage.ts b/Playwright základy na GitHub/page-objects/LoginPage.ts
new file mode 100644
index 0000000..9720203
--- /dev/null
+++ b/Playwright základy na GitHub/page-objects/LoginPage.ts
@@ -0,0 +1,72 @@
+
+// import třída modulů které budeme potřebovat
+import { Locator, Page
+ } from "@playwright/test";
+
+//definujeme třídy (class)
+export class LoginPage{
+ page: Page;
+ userNameInput: Locator;
+ passwordInput: Locator;
+ loginButton: Locator;
+ invalidCredentialsErrorMessage: Locator;
+ requiredCredentialsErrorMassage: Locator;
+ lockedOutErrorMassage: Locator;
+
+ // vytvoříme konstruktor třídy definuje proměnou page
+ constructor(page: Page) {
+ this.page = page;
+ this.userNameInput = page.locator('#user-name');
+ this.passwordInput = page.locator('#password');
+ this.loginButton = page.locator('#login-button');
+ this.invalidCredentialsErrorMessage = page.getByText('Epic sadface: Username and password do not match any user in this service');
+ this.requiredCredentialsErrorMassage = page.getByText('Epic sadface: Username is required');
+ this.lockedOutErrorMassage = page.getByText('')
+ this.lockedOutErrorMassage = page.getByText('Epic sadface: Sorry, this user has been locked out.');
+ }
+
+ // teď si definujeme metody na práci s elementy výše this.userNameInput = page.locator('#user-name');
+ // metoda na přejetí na stránku s Loginem
+ async gotoLoginPage() {
+ await this.page.goto('https://www.saucedemo.com/');
+ }
+
+ // metoda přihlašovacího jména
+ // protože chceme vepsat uživatelské jmáno použijeme metodu fill
+ async enterValidUsername() {
+ await this.userNameInput.fill('standard_user');
+ }
+
+ async enterLockedOutUser() {
+ await this.userNameInput.fill('locked_out_user');
+}
+
+ // alternativy k valid budou invalid
+ async enterInvalidUsername() {
+ await this.userNameInput.fill('jmeno');
+}
+
+ // zde zapíšeme heslo
+ async enterValidPassword() {
+ await this.passwordInput.fill('secret_sauce');
+ }
+
+ async enterInvalidPassword() {
+ await this.passwordInput.fill('heslo');
+ }
+
+ // klik na login button
+ async clickLoginButton() {
+ await this.loginButton.click();
+ }
+
+
+ // valid metody na ´ůspěšné přihlášení zapozdříme tímto způsobem:
+ async login() {
+ await this.userNameInput.fill('standard_user');
+ await this.passwordInput.fill('secret_sauce');
+ await this.loginButton.click();
+ }
+
+}
+
diff --git a/Playwright základy na GitHub/playwright.config.ts b/Playwright základy na GitHub/playwright.config.ts
new file mode 100644
index 0000000..cdcccfe
--- /dev/null
+++ b/Playwright základy na GitHub/playwright.config.ts
@@ -0,0 +1,77 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ..devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/Playwright základy na GitHub/tests-examples/demo-todo-app.spec.ts b/Playwright základy na GitHub/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/Playwright základy na GitHub/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright základy na GitHub/tests/alerts.spec.ts b/Playwright základy na GitHub/tests/alerts.spec.ts
new file mode 100644
index 0000000..e69de29
diff --git a/Playwright základy na GitHub/tests/assertions.spec.ts b/Playwright základy na GitHub/tests/assertions.spec.ts
new file mode 100644
index 0000000..15d7058
--- /dev/null
+++ b/Playwright základy na GitHub/tests/assertions.spec.ts
@@ -0,0 +1,27 @@
+import { test, expect } from '@playwright/test';
+
+//pokud zapíšeme za test.only spustíme pouze jeden tento test
+test('Element state', async ({ page }) => {
+ await page.goto('https://www.saucedemo.com');
+
+ //Vypíšeme zda stav elementu jestli je viditelný
+ await expect(page.locator('#user-name')).toBeVisible();
+ //await expect(page.locator('#user-name')).toContain;
+
+ //Vypíšeme zda stav elementu jestli je editovatelný
+ await expect(page.locator('#password')).toBeEditable();
+
+ //Vypíšeme zda stav elementu jestli je na stránce jedenkrát (to je číslo 1 v závorce)
+ await expect(page.locator('#login-button')).toHaveCount(1);
+
+ //Ověří že se některý element na stránce nenáchází. Přesný opak to.BeVisible. Na stránce by se neměl nacházet tento element
+ await expect(page.locator('#skillmea')).not.toBeVisible();
+
+});
+
+// toto je idetifikovatelný element -- > await page.locator('#user-name')
+// Pokud nám test někde padá nebo chci případně přeskočit místo kde padl použiji .soft v tomto místě přeskočí chybu a pkračuje dál
+// await expect.soft(page.locator('#password'))not.toBeEditable();
+
+// seznam všech asertů můžeš získat že za tešku vložím .toBeHave nebou .toBeContain
+//await expect(page.locator('#user-name')).toContain;
\ No newline at end of file
diff --git a/Playwright základy na GitHub/tests/elementState.spec.ts b/Playwright základy na GitHub/tests/elementState.spec.ts
new file mode 100644
index 0000000..385e360
--- /dev/null
+++ b/Playwright základy na GitHub/tests/elementState.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+//pokud zapíšeme za test.only spustíme pouze jeden tento test
+test('Element state', async ({ page }) => {
+ await page.goto('https://www.saucedemo.com');
+
+ //Vypíšeme zda stav elementu jestli je editovatelný
+ console.log(await page.locator('#user-name').isEditable());
+
+ //Vypíšeme zda stav elementu jestli je viditelný
+ console.log(await page.locator('#password').isVisible());
+
+ //Vypíšeme zda stav elementu jestli je skrytý
+ console.log(await page.locator('#login-button').isHidden());
+
+
+
+});
\ No newline at end of file
diff --git a/Playwright základy na GitHub/tests/example.spec copy.ts b/Playwright základy na GitHub/tests/example.spec copy.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/Playwright základy na GitHub/tests/example.spec copy.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/Playwright základy na GitHub/tests/example.spec.ts b/Playwright základy na GitHub/tests/example.spec.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/Playwright základy na GitHub/tests/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/Playwright základy na GitHub/tests/home.spec.ts b/Playwright základy na GitHub/tests/home.spec.ts
new file mode 100644
index 0000000..b3521f2
--- /dev/null
+++ b/Playwright základy na GitHub/tests/home.spec.ts
@@ -0,0 +1,29 @@
+import { test, expect } from '@playwright/test';
+import { LoginPage } from '../page-objects/LoginPage';
+import { HomePage } from '../page-objects/HomePage';
+
+// přihlášení na HomePage a ověření textu domovské stránky.
+test('Verify home title', async ({ page }) => {
+ const loginPage = new LoginPage(page);
+ const homePage = new HomePage(page);
+ await loginPage.gotoLoginPage();
+ await loginPage.login();
+ await expect(homePage.title).toBeVisible();
+});
+
+// test přidání produktu do košíku a kontrola že zobrazuje 1 položka v košíku
+test('Verify add to card functionality', async ({ page }) => {
+ const loginPage = new LoginPage(page);
+ const homePage = new HomePage(page);
+ await loginPage.gotoLoginPage();
+ await loginPage.login();
+ await homePage.clickOnAddToCart();
+ await expect(homePage.cardBadge).toHaveText("1");
+});
+
+//Odstranit produkt z košíká zkontrolovat že v něm není
+
+//vytvořit novou třídu pro produkt page, identifikovat elementy, napsat test na přidání do košíku product page, kliknutína tlačítko back ověřím že se dostanu zpět na homePage
+
+
+
diff --git a/Playwright základy na GitHub/tests/login.spec.ts b/Playwright základy na GitHub/tests/login.spec.ts
new file mode 100644
index 0000000..aa80cba
--- /dev/null
+++ b/Playwright základy na GitHub/tests/login.spec.ts
@@ -0,0 +1,79 @@
+/*
+ Zápis pomocí Page_Objects --> je kratší, čitatelnější a přehlednější. Pokud se například změní lokátor id nemusím ho měnit ve všech testech
+ale, v daném page-objects
+ctrl + click mě přesune na zadanou metodu!!
+ */
+import { test, expect } from '@playwright/test';
+//Importujeme vytvořenou třídu z page-objects
+import { LoginPage } from '../page-objects/LoginPage';
+
+test('Successful login', async ({ page }) => {
+ // nová instance loginPage odkazuje na metody co jsme si vytvořili v LoginPage
+ const loginPage = new LoginPage(page);
+ // zadáme metody co jsme vytvořily v LoginPage
+ await loginPage.gotoLoginPage();
+ // místo tohoto použijeme námi vytvořenou metodu login z LoginPage
+ //await loginPage.enterValidUsername();
+ //await loginPage.enterValidPassword();
+ //await loginPage.clickLoginButton();
+ // metoda login
+ await loginPage.login();
+ // ověříme že jsme se úspěšně přihlásily
+ await expect(page).toHaveURL('https://www.saucedemo.com/inventory.html');
+ /*
+ // způsob jeden test komplet v kódu bez odkazu na page-objects
+ await page.goto('https://www.saucedemo.com/');
+ await page.locator('[data-test="username"]').click();
+ await page.locator('[data-test="username"]').fill('standard_user');
+ await page.locator('[data-test="password"]').click();
+ await page.locator('[data-test="password"]').fill('secret_sauce');
+ await page.locator('[data-test="login-button"]').click();
+
+ // Zkontrolujeme jestli na stránce je viditelný Swag Labs
+ await expect(page.getByText('Swag Labs')).toBeVisible();
+ */
+});
+
+// test přihlášení s neplatným heslem
+test('Cannot login with valid username and invalid password', async ({ page }) => {
+ // test reporty(video 23):
+
+ const loginPage = new LoginPage(page);
+ await loginPage.gotoLoginPage();
+ await loginPage.enterValidUsername();
+ await loginPage.enterInvalidPassword();
+ await loginPage.clickLoginButton();
+ await expect(loginPage.invalidCredentialsErrorMessage).toBeVisible();
+});
+
+
+ //test s neplatným přihlašovacím jménem
+ test('Cannot login with invalid username and valid password', async ({ page }) => {
+ const loginPage = new LoginPage(page);
+ await loginPage.gotoLoginPage();
+ await loginPage.enterInvalidUsername();
+ await loginPage.enterValidPassword();
+ await loginPage.clickLoginButton();
+ await expect(loginPage.invalidCredentialsErrorMessage).toBeVisible();
+
+});
+
+//test pouze s kliknutím na login button
+test('Cannot login with blank fields', async ({ page }) => {
+ const loginPage = new LoginPage(page);
+ await loginPage.gotoLoginPage();
+ await loginPage.clickLoginButton();
+ await expect(loginPage.requiredCredentialsErrorMassage).toBeVisible();
+
+});
+
+// přihlášení s zablokovaným userem
+test('Cannot login with locked out user', async ({ page }) => {
+ const loginPage = new LoginPage(page);
+ await loginPage.gotoLoginPage();
+ await loginPage.enterLockedOutUser();
+ await loginPage.enterValidPassword();
+ await loginPage.clickLoginButton();
+ await expect(loginPage.lockedOutErrorMassage).toBeVisible();
+
+});
diff --git a/Playwright základy na GitHub/tests/screenshots.spec.ts b/Playwright základy na GitHub/tests/screenshots.spec.ts
new file mode 100644
index 0000000..e69de29
diff --git a/Playwright základy na GitHub/tests/tabs.spec.ts b/Playwright základy na GitHub/tests/tabs.spec.ts
new file mode 100644
index 0000000..e69de29
diff --git a/Playwright základy na GitHub/tests/upload.spec.ts b/Playwright základy na GitHub/tests/upload.spec.ts
new file mode 100644
index 0000000..e69de29
diff --git a/Playwright základy na GitHub/Úkoly.md b/Playwright základy na GitHub/Úkoly.md
new file mode 100644
index 0000000..7235adb
--- /dev/null
+++ b/Playwright základy na GitHub/Úkoly.md
@@ -0,0 +1,13 @@
+1. vytvořit novou třídu pro produkt page, identifikovat elementy, napsat test na přidání do košíku product page, kliknutína tlačítko back ověřím že se dostanu zpět na homePage
+
+2. product page --> //vytvořit novou třídu pro produkt page, identifikovat elementy, napsat test na přidání do košíku product page, kliknutína tlačítko back ověřím že se dostanu zpět na homePage
+
+3. fixtures dodělat video 21!!
+
+4. report test --> test stepy
+
+5. video 26 alerty udělat
+
+6. Od 21 vše dodělat!!
+
+
diff --git a/Playwright základy na GitHub/Čo je Page Object Model (POM).md b/Playwright základy na GitHub/Čo je Page Object Model (POM).md
new file mode 100644
index 0000000..695731e
--- /dev/null
+++ b/Playwright základy na GitHub/Čo je Page Object Model (POM).md
@@ -0,0 +1,9 @@
+Page Object Model (POM) je návrhový vzor v test automatizácii, ktorý slúži na organizáciu a správu automatizovaných testov v testovacej sade. Tento vzor zabezpečuje oddelenie testovacieho kódu od samotnej implementácie testovaných stránok.
+
+
+Konkrétne, POM rozdeľuje testovanie webovej aplikácie na dva oddelené kódy:
+
+Testovací kód: tento kód zahŕňa logiku testovania, ako sú interakcie s webovými stránkami, overenie správnosti vykonania akcií a overenie výsledkov testovania.
+Kód objektov stránky (Page Objects): tento kód zahŕňa definície objektov, ktoré predstavujú prvky na webovej stránke (napr. tlačidlá, polia na vstupe, odkazy atď.) a ich správanie. Každý objekt stránky má svoju vlastnú triedu, ktorá implementuje metódy pre interakciu s príslušnými prvky na stránke.
+
+Výhodou použitia POM je to, že testovací kód sa stáva jednoduchším a ľahšie udržiavateľným, pretože neobsahuje žiadne informácie o interakcii s prvkami na webovej stránke. Kód objektov stránky poskytuje abstraktnú reprezentáciu prvkov na webovej stránke, ktorá je potom využívaná testovacím kódom. Tento prístup umožňuje jednoduchšiu údržbu automatizovaných testov v prípade zmien na webovej stránke, pretože zmeny sa robia len v kóde objektov stránky.
\ No newline at end of file
diff --git a/Playwright_GH_TS/...github/workflows/search_google.yml b/Playwright_GH_TS/...github/workflows/search_google.yml
new file mode 100644
index 0000000..7ab226a
--- /dev/null
+++ b/Playwright_GH_TS/...github/workflows/search_google.yml
@@ -0,0 +1,37 @@
+name: Search Google Tests
+
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ path: 'Playwright_GH_TS'
+ - uses: actions/setup-node@v2
+ with:
+ node-version: 16
+ - name: Install dependencies
+ run: |
+ cd Playwright_GH_TS/search_google
+ npm ci
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: |
+ cd Playwright_GH_TS/search_google
+ npx playwright test
+ - uses: actions/upload-artifact@v2
+ if: always()
+ with:
+ name: playwright-report
+ path: cd Playwright_GH_TS/search_google/playwright-report/
+ retention-days: 90
+
diff --git a/Playwright_GH_TS/...github/workflows/zive.yml b/Playwright_GH_TS/...github/workflows/zive.yml
new file mode 100644
index 0000000..66aef33
--- /dev/null
+++ b/Playwright_GH_TS/...github/workflows/zive.yml
@@ -0,0 +1,37 @@
+name: Zive Tests
+
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ path: 'Playwright_GH_TS'
+ - uses: actions/setup-node@v2
+ with:
+ node-version: 16
+ - name: Install dependencies
+ run: |
+ cd Playwright_GH_TS/zive
+ npm ci
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: |
+ cd Playwright_GH_TS/zive
+ npx playwright test
+ - uses: actions/upload-artifact@v2
+ if: always()
+ with:
+ name: playwright-report
+ path: cd Playwright_GH_TS/zive/playwright-report/
+ retention-days: 90
+
diff --git a/Playwright_GH_TS/aaa/.gitignore b/Playwright_GH_TS/aaa/.gitignore
new file mode 100644
index 0000000..75e854d
--- /dev/null
+++ b/Playwright_GH_TS/aaa/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/Playwright_GH_TS/aaa/package-lock.json b/Playwright_GH_TS/aaa/package-lock.json
new file mode 100644
index 0000000..a9226bc
--- /dev/null
+++ b/Playwright_GH_TS/aaa/package-lock.json
@@ -0,0 +1,67 @@
+{
+ "name": "aaa",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "aaa",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.36.2"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.36.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.2.tgz",
+ "integrity": "sha512-2rVZeyPRjxfPH6J0oGJqE8YxiM1IBRyM8hyrXYK7eSiAqmbNhxwcLa7dZ7fy9Kj26V7FYia5fh9XJRq4Dqme+g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.36.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.4.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz",
+ "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.36.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.2.tgz",
+ "integrity": "sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ }
+ }
+}
diff --git a/Playwright_GH_TS/aaa/package.json b/Playwright_GH_TS/aaa/package.json
new file mode 100644
index 0000000..efdc99d
--- /dev/null
+++ b/Playwright_GH_TS/aaa/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "aaa",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.36.2"
+ }
+}
diff --git a/Playwright_GH_TS/aaa/playwright.config.ts b/Playwright_GH_TS/aaa/playwright.config.ts
new file mode 100644
index 0000000..301801e
--- /dev/null
+++ b/Playwright_GH_TS/aaa/playwright.config.ts
@@ -0,0 +1,77 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/Playwright_GH_TS/aaa/tests-examples/demo-todo-app.spec.ts b/Playwright_GH_TS/aaa/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/Playwright_GH_TS/aaa/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright_GH_TS/aaa/tests/aa.spec.ts b/Playwright_GH_TS/aaa/tests/aa.spec.ts
new file mode 100644
index 0000000..66af5d1
--- /dev/null
+++ b/Playwright_GH_TS/aaa/tests/aa.spec.ts
@@ -0,0 +1,26 @@
+const { chrome } = require('playwright');
+
+(async () => {
+ const browser = await chrome.launch({
+ headless: false
+ });
+ const context = await browser.newContext();
+ const page = await context.newPage();
+ await page.goto('https://www.aaaauto.cz/');
+ await page.getByRole('button', { name: 'Přijmout vše' }).click();
+ await page.getByRole('button', { name: 'Značka Vyberte značku' }).click();
+ await page.getByText('Škoda (2793)').first().click();
+ await page.getByRole('button', { name: 'Model Vyberte model' }).click();
+ await page.getByText('Citigo(40)').click();
+ await page.getByRole('button', { name: 'Rok Vyberte stáří vozu' }).click();
+ await page.getByText('Do 10 let').click();
+ await page.getByRole('button', { name: 'Cena Vyberte cenu' }).click();
+ await page.getByText('Do 200 000 Kč', { exact: true }).click();
+ await page.getByRole('button', { name: 'Kategorie Vyberte kategorii' }).click();
+ await page.getByText('Úsporné vozy (31)').click();
+ await page.getByRole('button', { name: 'Hledat' }).click();
+
+ // ---------------------
+ await context.close();
+ await browser.close();
+})();
\ No newline at end of file
diff --git a/Playwright_GH_TS/aaa/tests/aaa.spec.ts b/Playwright_GH_TS/aaa/tests/aaa.spec.ts
new file mode 100644
index 0000000..0ac2afc
--- /dev/null
+++ b/Playwright_GH_TS/aaa/tests/aaa.spec.ts
@@ -0,0 +1,17 @@
+import { test, expect } from '@playwright/test';
+
+test('test', async ({ page }) => {
+ await page.goto('https://www.aaaauto.cz/');
+ await page.getByRole('button', { name: 'Přijmout vše' }).click();
+ await page.getByRole('button', { name: 'Značka Vyberte značku' }).click();
+ await page.getByText('Škoda', { exact: true }).first().click();
+ await page.getByRole('button', { name: 'Model Vyberte model' }).click();
+ await page.getByText('Citigo(40)').click();
+ await page.getByRole('button', { name: 'Rok Vyberte stáří vozu' }).click();
+ await page.getByText('Do 10 let').click();
+ await page.getByRole('button', { name: 'Cena Vyberte cenu' }).click();
+ await page.getByText('Do 200 000 Kč', { exact: true }).click();
+ await page.getByRole('button', { name: 'Kategorie Vyberte kategorii' }).click();
+ await page.locator('#hpFilterNG').getByText('Úsporné vozy').click();
+ await page.getByRole('button', { name: 'Hledat' }).click();
+});
\ No newline at end of file
diff --git a/Playwright_GH_TS/aaa/tests/example.spec.ts b/Playwright_GH_TS/aaa/tests/example.spec.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/Playwright_GH_TS/aaa/tests/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/Playwright_GH_TS/lukan/.gitignore b/Playwright_GH_TS/lukan/.gitignore
new file mode 100644
index 0000000..6d284fa
--- /dev/null
+++ b/Playwright_GH_TS/lukan/.gitignore
@@ -0,0 +1,7 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/Playwright_GH_TS/lukan/e2e/example.spec.ts b/Playwright_GH_TS/lukan/e2e/example.spec.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/Playwright_GH_TS/lukan/e2e/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/Playwright_GH_TS/lukan/package-lock.json b/Playwright_GH_TS/lukan/package-lock.json
new file mode 100644
index 0000000..c41e1fc
--- /dev/null
+++ b/Playwright_GH_TS/lukan/package-lock.json
@@ -0,0 +1,67 @@
+{
+ "name": "lukan",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "lukan",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.36.2"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.36.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.2.tgz",
+ "integrity": "sha512-2rVZeyPRjxfPH6J0oGJqE8YxiM1IBRyM8hyrXYK7eSiAqmbNhxwcLa7dZ7fy9Kj26V7FYia5fh9XJRq4Dqme+g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.36.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.2.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
+ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.36.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.2.tgz",
+ "integrity": "sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ }
+ }
+}
diff --git a/Playwright_GH_TS/lukan/package.json b/Playwright_GH_TS/lukan/package.json
new file mode 100644
index 0000000..4fe98a6
--- /dev/null
+++ b/Playwright_GH_TS/lukan/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "lukan",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.36.2"
+ }
+}
diff --git a/Playwright_GH_TS/lukan/page-objects/HomePage.ts b/Playwright_GH_TS/lukan/page-objects/HomePage.ts
new file mode 100644
index 0000000..5799dc5
--- /dev/null
+++ b/Playwright_GH_TS/lukan/page-objects/HomePage.ts
@@ -0,0 +1,62 @@
+import { Locator, Page
+} from "@playwright/test";
+export class HomePage{
+ page: Page;
+ cookiesButtonAccept: Locator;
+ cookiesButtonDecline: Locator;
+ menuButtonUvodniStrana: Locator;
+ menuButtonOMne: Locator;
+ menuButtonZasadyOchranyOsobnichUdaju: Locator;
+ menuButtonPodporovatele: Locator;
+ searchButton: Locator;
+ searchFieldInput: Locator;
+
+ constructor(page: Page){
+ this.page = page;
+ this.cookiesButtonAccept = page.getByText('Accept');
+ this.cookiesButtonDecline = page.getByText('Decline');
+ this.menuButtonUvodniStrana = page.getByRole('link', { name: 'Úvodní stránka' });
+ this.menuButtonOMne = page.getByRole('link' , { name: 'O Mně' });
+ this.menuButtonZasadyOchranyOsobnichUdaju = page.getByRole('link' , { name: 'Zásady ochrany osobních údajů' });
+ this.menuButtonPodporovatele = page.getByRole('link' , { name: 'Podporovatelé' });
+ this.searchFieldInput = page.locator('//*[@id="search-7"]/form/label/input');
+ this.searchButton = page.locator('#search-7 > form > button > svg > use');
+ }
+
+ async gotoHome() {
+ await this.page.goto('https://www.lukan.cz/');
+ }
+
+ async clickCookiesButtonAccept() {
+ await this.cookiesButtonAccept.click();
+ }
+
+ async clickCookiesButtonDecline() {
+ await this.cookiesButtonDecline.click();
+ }
+
+ async clickUvodniStranaButton() {
+ await this.menuButtonUvodniStrana.click();
+ }
+
+ async clickOMneButton() {
+ await this.menuButtonOMne.click();
+ }
+
+ async clickZOOUButton() {
+ await this.menuButtonZasadyOchranyOsobnichUdaju.click();
+ }
+
+ async clickPodporovateleButton() {
+ await this.menuButtonPodporovatele.click();
+ }
+ async enterTextSearchFields() {
+ await this.searchFieldInput.fill('Test');
+ }
+
+ async clickSearchButton() {
+ await this.searchButton.click();
+ }
+
+
+};
\ No newline at end of file
diff --git a/Playwright_GH_TS/lukan/page-objects/LoginPage.ts b/Playwright_GH_TS/lukan/page-objects/LoginPage.ts
new file mode 100644
index 0000000..d4711a7
--- /dev/null
+++ b/Playwright_GH_TS/lukan/page-objects/LoginPage.ts
@@ -0,0 +1,5 @@
+import { Locator, Page
+} from "@playwright/test";
+export class LoginPage{
+
+}
\ No newline at end of file
diff --git a/Playwright_GH_TS/lukan/playwright.config.ts b/Playwright_GH_TS/lukan/playwright.config.ts
new file mode 100644
index 0000000..34956d0
--- /dev/null
+++ b/Playwright_GH_TS/lukan/playwright.config.ts
@@ -0,0 +1,77 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ /*
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+*/
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ..devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/Playwright_GH_TS/lukan/tests-examples/demo-todo-app.spec.ts b/Playwright_GH_TS/lukan/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/Playwright_GH_TS/lukan/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright_GH_TS/lukan/tests/cookies.test.spec.ts b/Playwright_GH_TS/lukan/tests/cookies.test.spec.ts
new file mode 100644
index 0000000..aaf84da
--- /dev/null
+++ b/Playwright_GH_TS/lukan/tests/cookies.test.spec.ts
@@ -0,0 +1,19 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+
+// test odsouhlasení cookies
+test('HomePage click Accept cookies', async ({ page }) => {
+
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.clickCookiesButtonAccept();
+ });
+
+
+ // test zamítnutí cookies
+test('HomaPage click Decline cookies' , async ({page}) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.clickCookiesButtonDecline();
+})
\ No newline at end of file
diff --git a/Playwright_GH_TS/lukan/tests/example.spec.ts b/Playwright_GH_TS/lukan/tests/example.spec.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/Playwright_GH_TS/lukan/tests/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/Playwright_GH_TS/lukan/tests/login.spec.ts b/Playwright_GH_TS/lukan/tests/login.spec.ts
new file mode 100644
index 0000000..e69de29
diff --git a/Playwright_GH_TS/lukan/tests/menuButton.test.spec.ts b/Playwright_GH_TS/lukan/tests/menuButton.test.spec.ts
new file mode 100644
index 0000000..d4cb240
--- /dev/null
+++ b/Playwright_GH_TS/lukan/tests/menuButton.test.spec.ts
@@ -0,0 +1,14 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+test('Click menu button with HomePage' , async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.clickUvodniStranaButton();
+ await homePage.clickOMneButton();
+ await homePage.clickZOOUButton();
+ await homePage.clickPodporovateleButton();
+
+ //await context.close();
+ //await browser.close();
+});
\ No newline at end of file
diff --git a/Playwright_GH_TS/lukan/tests/search.spec.ts b/Playwright_GH_TS/lukan/tests/search.spec.ts
new file mode 100644
index 0000000..0f781be
--- /dev/null
+++ b/Playwright_GH_TS/lukan/tests/search.spec.ts
@@ -0,0 +1,10 @@
+import { test, expect } from '@playwright/test';
+import { HomePage } from '../page-objects/HomePage';
+
+test('Search web' , async ({ page }) => {
+ const homePage = new HomePage(page);
+ await homePage.gotoHome();
+ await homePage.enterTextSearchFields();
+ await homePage.clickSearchButton();
+
+});
\ No newline at end of file
diff --git a/Playwright_GH_TS/lukan/tests/test-1.spec.ts b/Playwright_GH_TS/lukan/tests/test-1.spec.ts
new file mode 100644
index 0000000..29fdff8
--- /dev/null
+++ b/Playwright_GH_TS/lukan/tests/test-1.spec.ts
@@ -0,0 +1,6 @@
+import { test, expect } from '@playwright/test';
+
+test('test', async ({ page }) => {
+ await page.goto('https://lukan.cz/');
+ await page.getByRole('link', { name: 'Úvodní stránka' }).click();
+});
\ No newline at end of file
diff --git a/Playwright_GH_TS/search_google/.gitignore b/Playwright_GH_TS/search_google/.gitignore
new file mode 100644
index 0000000..40a2227
--- /dev/null
+++ b/Playwright_GH_TS/search_google/.gitignore
@@ -0,0 +1,10 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/Playwright_GH_TS/search_google/package-lock.json b/Playwright_GH_TS/search_google/package-lock.json
new file mode 100644
index 0000000..f8e68b1
--- /dev/null
+++ b/Playwright_GH_TS/search_google/package-lock.json
@@ -0,0 +1,67 @@
+{
+ "name": "search_google",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "search_google",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.31.2"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.31.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
+ "integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.31.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.13.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
+ "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.31.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
+ "integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
+ "dev": true,
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ }
+ }
+}
diff --git a/Playwright_GH_TS/search_google/package.json b/Playwright_GH_TS/search_google/package.json
new file mode 100644
index 0000000..7240edc
--- /dev/null
+++ b/Playwright_GH_TS/search_google/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "search_google",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.31.2"
+ }
+}
diff --git a/Playwright_GH_TS/search_google/playwright.config.ts b/Playwright_GH_TS/search_google/playwright.config.ts
new file mode 100644
index 0000000..213b45f
--- /dev/null
+++ b/Playwright_GH_TS/search_google/playwright.config.ts
@@ -0,0 +1,90 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Maximum time one test can run for. */
+ timeout: 30 * 1000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 5000
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 0,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ /*
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+ */
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { channel: 'chrome' },
+ // },
+ ],
+
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ // outputDir: 'test-results/',
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // port: 3000,
+ // },
+});
diff --git a/Playwright_GH_TS/search_google/tests-examples/demo-todo-app.spec.ts b/Playwright_GH_TS/search_google/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/Playwright_GH_TS/search_google/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright_GH_TS/search_google/tests/example.spec.ts b/Playwright_GH_TS/search_google/tests/example.spec.ts
new file mode 100644
index 0000000..bd681a2
--- /dev/null
+++ b/Playwright_GH_TS/search_google/tests/example.spec.ts
@@ -0,0 +1,33 @@
+//****Tento projekt je TypeScript****//
+
+import { test, expect } from '@playwright/test';
+import { chromium } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ // const browser = await chromium.launch();
+ // const context = await browser.newContext();
+ await page.goto('https://www.google.com');
+
+ // Odsouhlasí cookies
+ await page.click("#W0wltc > div");
+
+ // Najdeme pole vyhledávání a napíšeme do něj slovo "test"
+ const searchInput = await page.$('[name="q"]');
+ await searchInput?.type('test');
+
+ // Potvrdíme vyhledávání stisknutím klávesy Enter
+ await searchInput?.press('Enter');
+
+ // Nebo můžeme kliknout na tlačítko pro vyhledávání
+ // const searchButton = await page.$('[name="btnK"]');
+ // await searchButton?.click();
+
+ // Vypíše název stránky do konzole
+ const title = await page.title();
+ console.log(title);
+
+ //await page.waitForNavigation();
+ // console.log('Search results page title:', await page.title());
+
+ // await browser.close();
+});
diff --git a/Playwright_GH_TS/zive.ts/.gitignore b/Playwright_GH_TS/zive.ts/.gitignore
new file mode 100644
index 0000000..75e854d
--- /dev/null
+++ b/Playwright_GH_TS/zive.ts/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/Playwright_GH_TS/zive.ts/package-lock.json b/Playwright_GH_TS/zive.ts/package-lock.json
new file mode 100644
index 0000000..dc57de5
--- /dev/null
+++ b/Playwright_GH_TS/zive.ts/package-lock.json
@@ -0,0 +1,67 @@
+{
+ "name": "zive.ts",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "zive.ts",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.31.2"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.31.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
+ "integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.31.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.15.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
+ "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.31.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
+ "integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
+ "dev": true,
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ }
+ }
+}
diff --git a/Playwright_GH_TS/zive.ts/package.json b/Playwright_GH_TS/zive.ts/package.json
new file mode 100644
index 0000000..82cccf6
--- /dev/null
+++ b/Playwright_GH_TS/zive.ts/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "zive.ts",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.31.2"
+ }
+}
diff --git a/Playwright_GH_TS/zive.ts/playwright.config.ts b/Playwright_GH_TS/zive.ts/playwright.config.ts
new file mode 100644
index 0000000..5e5154b
--- /dev/null
+++ b/Playwright_GH_TS/zive.ts/playwright.config.ts
@@ -0,0 +1,90 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Maximum time one test can run for. */
+ timeout: 30 * 1000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 5000
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 0,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { channel: 'chrome' },
+ // },
+ ],
+
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ // outputDir: 'test-results/',
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // port: 3000,
+ // },
+});
diff --git a/Playwright_GH_TS/zive.ts/tests-examples/demo-todo-app.spec.ts b/Playwright_GH_TS/zive.ts/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/Playwright_GH_TS/zive.ts/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright_GH_TS/zive.ts/tests/example.spec.ts b/Playwright_GH_TS/zive.ts/tests/example.spec.ts
new file mode 100644
index 0000000..ecbe392
--- /dev/null
+++ b/Playwright_GH_TS/zive.ts/tests/example.spec.ts
@@ -0,0 +1,11 @@
+import { test, expect } from '@playwright/test';
+
+test('test', async ({ page }) => {
+ await page.goto('https://www.zive.cz/');
+ await page.getByRole('button', { name: 'Souhlasit a zavřít: Souhlasit s naším zpracováním údajů a zavřít' }).click();
+ await page.getByRole('link', { name: 'Menu' }).click();
+ await page.getByPlaceholder('Hledat na Živě').click();
+ await page.getByPlaceholder('Hledat na Živě').fill('zive');
+ await page.goto('https://www.zive.cz/vysledky-vyhledavani/sc-236/?q=zive');
+ await page.getByRole('link', { name: 'O počítačích, IT a internetu - Živě.cz' }).click();
+});
\ No newline at end of file
diff --git a/Playwright_GH_TS/zive/.gitignore b/Playwright_GH_TS/zive/.gitignore
new file mode 100644
index 0000000..6d284fa
--- /dev/null
+++ b/Playwright_GH_TS/zive/.gitignore
@@ -0,0 +1,7 @@
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/Playwright_GH_TS/zive/e2e/example.spec.ts b/Playwright_GH_TS/zive/e2e/example.spec.ts
new file mode 100644
index 0000000..c511525
--- /dev/null
+++ b/Playwright_GH_TS/zive/e2e/example.spec.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects the URL to contain intro.
+ await expect(page).toHaveURL(/.*intro/);
+});
diff --git a/Playwright_GH_TS/zive/package-lock.json b/Playwright_GH_TS/zive/package-lock.json
new file mode 100644
index 0000000..df8ded4
--- /dev/null
+++ b/Playwright_GH_TS/zive/package-lock.json
@@ -0,0 +1,67 @@
+{
+ "name": "zive",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "zive",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.31.2"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.31.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
+ "integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.31.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.15.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
+ "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.31.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
+ "integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
+ "dev": true,
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ }
+ }
+}
diff --git a/Playwright_GH_TS/zive/package.json b/Playwright_GH_TS/zive/package.json
new file mode 100644
index 0000000..caf7bbc
--- /dev/null
+++ b/Playwright_GH_TS/zive/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "zive",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "directories": {
+ "test": "tests"
+ },
+ "scripts": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@playwright/test": "^1.31.2"
+ }
+}
diff --git a/Playwright_GH_TS/zive/playwright.config.ts b/Playwright_GH_TS/zive/playwright.config.ts
new file mode 100644
index 0000000..fbf188a
--- /dev/null
+++ b/Playwright_GH_TS/zive/playwright.config.ts
@@ -0,0 +1,90 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './e2e',
+ /* Maximum time one test can run for. */
+ timeout: 30 * 1000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 5000
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 0,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { channel: 'chrome' },
+ // },
+ ],
+
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ // outputDir: 'test-results/',
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // port: 3000,
+ // },
+});
diff --git a/Playwright_GH_TS/zive/tests-examples/demo-todo-app.spec.ts b/Playwright_GH_TS/zive/tests-examples/demo-todo-app.spec.ts
new file mode 100644
index 0000000..2fd6016
--- /dev/null
+++ b/Playwright_GH_TS/zive/tests-examples/demo-todo-app.spec.ts
@@ -0,0 +1,437 @@
+import { test, expect, type Page } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+ 'buy some cheese',
+ 'feed the cat',
+ 'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+ test('should allow me to add todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create 1st todo.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Make sure the list only has one todo item.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0]
+ ]);
+
+ // Create 2nd todo.
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+
+ // Make sure the list now has two todo items.
+ await expect(page.getByTestId('todo-title')).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[1]
+ ]);
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+
+ test('should clear text input field when an item is added', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create one todo item.
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ // Check that input is empty.
+ await expect(newTodo).toBeEmpty();
+ await checkNumberOfTodosInLocalStorage(page, 1);
+ });
+
+ test('should append new items to the bottom of the list', async ({ page }) => {
+ // Create 3 items.
+ await createDefaultTodos(page);
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ // Check test using different methods.
+ await expect(page.getByText('3 items left')).toBeVisible();
+ await expect(todoCount).toHaveText('3 items left');
+ await expect(todoCount).toContainText('3');
+ await expect(todoCount).toHaveText(/3/);
+
+ // Check all items in one call.
+ await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+});
+
+test.describe('Mark all as completed', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test.afterEach(async ({ page }) => {
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to mark all items as completed', async ({ page }) => {
+ // Complete all todos.
+ await page.getByLabel('Mark all as complete').check();
+
+ // Ensure all todos have 'completed' class.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+ });
+
+ test('should allow me to clear the complete state of all items', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ // Check and then immediately uncheck.
+ await toggleAll.check();
+ await toggleAll.uncheck();
+
+ // Should be no completed classes.
+ await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+ });
+
+ test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+ const toggleAll = page.getByLabel('Mark all as complete');
+ await toggleAll.check();
+ await expect(toggleAll).toBeChecked();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Uncheck first todo.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').uncheck();
+
+ // Reuse toggleAll locator and make sure its not checked.
+ await expect(toggleAll).not.toBeChecked();
+
+ await firstTodo.getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+ // Assert the toggle all is checked again.
+ await expect(toggleAll).toBeChecked();
+ });
+});
+
+test.describe('Item', () => {
+
+ test('should allow me to mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ // Check first item.
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ await firstTodo.getByRole('checkbox').check();
+ await expect(firstTodo).toHaveClass('completed');
+
+ // Check second item.
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ await expect(secondTodo).not.toHaveClass('completed');
+ await secondTodo.getByRole('checkbox').check();
+
+ // Assert completed class.
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).toHaveClass('completed');
+ });
+
+ test('should allow me to un-mark items as complete', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // Create two items.
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const firstTodo = page.getByTestId('todo-item').nth(0);
+ const secondTodo = page.getByTestId('todo-item').nth(1);
+ const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+ await firstTodoCheckbox.check();
+ await expect(firstTodo).toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await firstTodoCheckbox.uncheck();
+ await expect(firstTodo).not.toHaveClass('completed');
+ await expect(secondTodo).not.toHaveClass('completed');
+ await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+ });
+
+ test('should allow me to edit an item', async ({ page }) => {
+ await createDefaultTodos(page);
+
+ const todoItems = page.getByTestId('todo-item');
+ const secondTodo = todoItems.nth(1);
+ await secondTodo.dblclick();
+ await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ // Explicitly assert the new text value.
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2]
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+});
+
+test.describe('Editing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should hide other controls when editing', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item').nth(1);
+ await todoItem.dblclick();
+ await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+ await expect(todoItem.locator('label', {
+ hasText: TODO_ITEMS[1],
+ })).not.toBeVisible();
+ await checkNumberOfTodosInLocalStorage(page, 3);
+ });
+
+ test('should save edits on blur', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should trim entered text', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ 'buy some sausages',
+ TODO_ITEMS[2],
+ ]);
+ await checkTodosInLocalStorage(page, 'buy some sausages');
+ });
+
+ test('should remove the item if an empty text string was entered', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+ await expect(todoItems).toHaveText([
+ TODO_ITEMS[0],
+ TODO_ITEMS[2],
+ ]);
+ });
+
+ test('should cancel edits on escape', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).dblclick();
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+ await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+ await expect(todoItems).toHaveText(TODO_ITEMS);
+ });
+});
+
+test.describe('Counter', () => {
+ test('should display the current number of todo items', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ // create a todo count locator
+ const todoCount = page.getByTestId('todo-count')
+
+ await newTodo.fill(TODO_ITEMS[0]);
+ await newTodo.press('Enter');
+
+ await expect(todoCount).toContainText('1');
+
+ await newTodo.fill(TODO_ITEMS[1]);
+ await newTodo.press('Enter');
+ await expect(todoCount).toContainText('2');
+
+ await checkNumberOfTodosInLocalStorage(page, 2);
+ });
+});
+
+test.describe('Clear completed button', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ });
+
+ test('should display the correct text', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+ });
+
+ test('should remove completed items when clicked', async ({ page }) => {
+ const todoItems = page.getByTestId('todo-item');
+ await todoItems.nth(1).getByRole('checkbox').check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(todoItems).toHaveCount(2);
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should be hidden when there are no items that are completed', async ({ page }) => {
+ await page.locator('.todo-list li .toggle').first().check();
+ await page.getByRole('button', { name: 'Clear completed' }).click();
+ await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+ });
+});
+
+test.describe('Persistence', () => {
+ test('should persist its data', async ({ page }) => {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS.slice(0, 2)) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+
+ const todoItems = page.getByTestId('todo-item');
+ const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+ await firstTodoCheck.check();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+
+ // Ensure there is 1 completed item.
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ // Now reload.
+ await page.reload();
+ await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+ await expect(firstTodoCheck).toBeChecked();
+ await expect(todoItems).toHaveClass(['completed', '']);
+ });
+});
+
+test.describe('Routing', () => {
+ test.beforeEach(async ({ page }) => {
+ await createDefaultTodos(page);
+ // make sure the app had a chance to save updated todos in storage
+ // before navigating to a new view, otherwise the items can get lost :(
+ // in some frameworks like Durandal
+ await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+ });
+
+ test('should allow me to display active items', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await expect(todoItem).toHaveCount(2);
+ await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+ });
+
+ test('should respect the back button', async ({ page }) => {
+ const todoItem = page.getByTestId('todo-item');
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+ await test.step('Showing all items', async () => {
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ await test.step('Showing active items', async () => {
+ await page.getByRole('link', { name: 'Active' }).click();
+ });
+
+ await test.step('Showing completed items', async () => {
+ await page.getByRole('link', { name: 'Completed' }).click();
+ });
+
+ await expect(todoItem).toHaveCount(1);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(2);
+ await page.goBack();
+ await expect(todoItem).toHaveCount(3);
+ });
+
+ test('should allow me to display completed items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(1);
+ });
+
+ test('should allow me to display all items', async ({ page }) => {
+ await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+ await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+ await page.getByRole('link', { name: 'Active' }).click();
+ await page.getByRole('link', { name: 'Completed' }).click();
+ await page.getByRole('link', { name: 'All' }).click();
+ await expect(page.getByTestId('todo-item')).toHaveCount(3);
+ });
+
+ test('should highlight the currently applied filter', async ({ page }) => {
+ await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+ //create locators for active and completed links
+ const activeLink = page.getByRole('link', { name: 'Active' });
+ const completedLink = page.getByRole('link', { name: 'Completed' });
+ await activeLink.click();
+
+ // Page change - active items.
+ await expect(activeLink).toHaveClass('selected');
+ await completedLink.click();
+
+ // Page change - completed items.
+ await expect(completedLink).toHaveClass('selected');
+ });
+});
+
+async function createDefaultTodos(page: Page) {
+ // create a new todo locator
+ const newTodo = page.getByPlaceholder('What needs to be done?');
+
+ for (const item of TODO_ITEMS) {
+ await newTodo.fill(item);
+ await newTodo.press('Enter');
+ }
+}
+
+async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).length === e;
+ }, expected);
+}
+
+async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
+ return await page.waitForFunction(e => {
+ return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
+ }, expected);
+}
+
+async function checkTodosInLocalStorage(page: Page, title: string) {
+ return await page.waitForFunction(t => {
+ return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
+ }, title);
+}
diff --git a/Playwright_GH_TS/zive/tests/example.spec.ts b/Playwright_GH_TS/zive/tests/example.spec.ts
new file mode 100644
index 0000000..3914e7f
--- /dev/null
+++ b/Playwright_GH_TS/zive/tests/example.spec.ts
@@ -0,0 +1,44 @@
+import { test, expect } from '@playwright/test';
+import { chromium } from '@playwright/test';
+
+
+ test('has title', async ({ page }) => {
+
+ await page.goto('https://zive.cz');
+
+ // Odsouhlasí cookies
+ await page.click("//button[@id='didomi-notice-agree-button']/span");
+
+ // takto
+ //const button = await page.locator("//button[@id='didomi-notice-agree-button']");
+ //await button.click();
+
+ // Klikne na menu
+ await page.click("//a[contains(text(),'Menu')]");
+
+ // vrátí se na hlavní stranu
+ await page.click("#mainFORM > nav > div > div.header > a.mn-logo");
+
+ // klikne do vyhledávání
+ await page.click("//a[@onclick='layout.menu.toggle(true)']");
+
+ // klikne do vyhledávacího pole
+ const searchInput = await page.$("#mainFORM > nav > div > div.header > div");
+ await searchInput?.type('test');
+
+ // Potvrdíme vyhledávání stisknutím klávesy Enter
+ await searchInput?.press('Enter');
+
+ // vrátíme se na hlavní stránku
+ await page.click('//*[@id="mainFORM"]/div[3]/header/div[2]/div/a');
+
+ // Zkontrolujeme, že stránka má správný název
+ const pageTitle = await page.title();
+ expect(pageTitle).toBe('Živě.cz – O počítačích, internetu, vědě a technice');
+
+
+
+
+
+
+ });