diff options
Diffstat (limited to 'bitbake/lib/toaster')
25 files changed, 895 insertions, 780 deletions
diff --git a/bitbake/lib/toaster/orm/fixtures/check_fixtures.py b/bitbake/lib/toaster/orm/fixtures/check_fixtures.py new file mode 100755 index 0000000000..ae3722e0f6 --- /dev/null +++ b/bitbake/lib/toaster/orm/fixtures/check_fixtures.py | |||
@@ -0,0 +1,38 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | # | ||
3 | # Copyright (C) 2025 Linux Foundation | ||
4 | # SPDX-License-Identifier: GPL-2.0-only | ||
5 | # | ||
6 | |||
7 | import json | ||
8 | import urllib.request | ||
9 | |||
10 | import gen_fixtures as fixtures | ||
11 | |||
12 | RELEASE_URL = "https://dashboard.yoctoproject.org/releases.json" | ||
13 | |||
14 | with urllib.request.urlopen(RELEASE_URL) as response: | ||
15 | if response.getcode() == 200: | ||
16 | data = response.read().decode("utf-8") | ||
17 | releases = json.loads(data) | ||
18 | else: | ||
19 | print("Couldn't access %s: %s" % (RELEASE_URL, reponse.getcode())) | ||
20 | exit(1) | ||
21 | |||
22 | |||
23 | # grab the recent release branches and add master, so we can ignore old branches | ||
24 | active_releases = [ | ||
25 | e["release_codename"].lower() for e in releases if e["series"] == "current" | ||
26 | ] | ||
27 | active_releases.append("master") | ||
28 | active_releases.append("head") | ||
29 | |||
30 | fixtures_releases = [x[0].lower() for x in fixtures.current_releases] | ||
31 | |||
32 | if set(active_releases) != set(fixtures_releases): | ||
33 | print("WARNING: Active releases don't match toaster configured releases, the difference is: %s" % set(active_releases).difference(set(fixtures_releases))) | ||
34 | print("Active releases: %s" % sorted(active_releases)) | ||
35 | print("Toaster configured releases: %s" % sorted(fixtures_releases)) | ||
36 | else: | ||
37 | print("Success, configuration matches") | ||
38 | |||
diff --git a/bitbake/lib/toaster/orm/fixtures/gen_fixtures.py b/bitbake/lib/toaster/orm/fixtures/gen_fixtures.py index 71afe3914e..6201f679b9 100755 --- a/bitbake/lib/toaster/orm/fixtures/gen_fixtures.py +++ b/bitbake/lib/toaster/orm/fixtures/gen_fixtures.py | |||
@@ -9,7 +9,7 @@ | |||
9 | # | 9 | # |
10 | # Edit the 'current_releases' table for each new release cycle | 10 | # Edit the 'current_releases' table for each new release cycle |
11 | # | 11 | # |
12 | # Usage: ./get_fixtures all | 12 | # Usage: ./get_fixtures --all |
13 | # | 13 | # |
14 | 14 | ||
15 | import os | 15 | import os |
@@ -35,19 +35,23 @@ verbose = False | |||
35 | # [Codename, Yocto Project Version, Release Date, Current Version, Support Level, Poky Version, BitBake branch] | 35 | # [Codename, Yocto Project Version, Release Date, Current Version, Support Level, Poky Version, BitBake branch] |
36 | current_releases = [ | 36 | current_releases = [ |
37 | # Release slot #1 | 37 | # Release slot #1 |
38 | ['Kirkstone','4.0','April 2022','4.0.8 (March 2023)','Stable - Long Term Support (until Apr. 2024)','','2.0'], | 38 | ['Scarthgap','5.0','April 2024','5.0.0 (April 2024)','Long Term Support (until April 2028)','','2.8'], |
39 | # Release slot #2 'local' | 39 | # Release slot #2 'local' |
40 | ['HEAD','HEAD','','Local Yocto Project','HEAD','','HEAD'], | 40 | ['HEAD','HEAD','','Local Yocto Project','HEAD','','HEAD'], |
41 | # Release slot #3 'master' | 41 | # Release slot #3 'master' |
42 | ['Master','master','','Yocto Project master','master','','master'], | 42 | ['Master','master','','Yocto Project master','master','','master'], |
43 | # Release slot #4 | 43 | # Release slot #4 |
44 | ['Mickledore','4.2','April 2023','4.2.0 (April 2023)','Support for 7 months (until October 2023)','','2.4'], | 44 | ['Whinlatter','5.3','October 2025','5.3.0 (October 2024)','Support for 7 months (until May 2026)','','2.14'], |
45 | # ['Langdale','4.1','October 2022','4.1.2 (January 2023)','Support for 7 months (until May 2023)','','2.2'], | 45 | ['Walnascar','5.2','April 2025','5.2.0 (April 2025)','Support for 7 months (until October 2025)','','2.12'], |
46 | # ['Honister','3.4','October 2021','3.4.2 (February 2022)','Support for 7 months (until May 2022)','26.0','1.52'], | 46 | #['Styhead','5.1','November 2024','5.1.0 (November 2024)','Support for 7 months (until May 2025)','','2.10'], |
47 | # ['Hardknott','3.3','April 2021','3.3.5 (March 2022)','Stable - Support for 13 months (until Apr. 2022)','25.0','1.50'], | 47 | #['Nanbield','4.3','November 2023','4.3.0 (November 2023)','Support for 7 months (until May 2024)','','2.6'], |
48 | # ['Gatesgarth','3.2','Oct 2020','3.2.4 (May 2021)','EOL','24.0','1.48'], | 48 | #['Mickledore','4.2','April 2023','4.2.0 (April 2023)','Support for 7 months (until October 2023)','','2.4'], |
49 | # Optional Release slot #5 | 49 | #['Langdale','4.1','October 2022','4.1.2 (January 2023)','Support for 7 months (until May 2023)','','2.2'], |
50 | ['Dunfell','3.1','April 2020','3.1.23 (February 2023)','Stable - Long Term Support (until Apr. 2024)','23.0','1.46'], | 50 | ['Kirkstone','4.0','April 2022','4.0.8 (March 2023)','Stable - Long Term Support (until Apr. 2024)','','2.0'], |
51 | #['Honister','3.4','October 2021','3.4.2 (February 2022)','Support for 7 months (until May 2022)','26.0','1.52'], | ||
52 | #['Hardknott','3.3','April 2021','3.3.5 (March 2022)','Stable - Support for 13 months (until Apr. 2022)','25.0','1.50'], | ||
53 | #['Gatesgarth','3.2','Oct 2020','3.2.4 (May 2021)','EOL','24.0','1.48'], | ||
54 | #['Dunfell','3.1','April 2020','3.1.23 (February 2023)','Stable - Long Term Support (until Apr. 2024)','23.0','1.46'], | ||
51 | ] | 55 | ] |
52 | 56 | ||
53 | default_poky_layers = [ | 57 | default_poky_layers = [ |
diff --git a/bitbake/lib/toaster/orm/fixtures/oe-core.xml b/bitbake/lib/toaster/orm/fixtures/oe-core.xml index 950f2a98af..264231d139 100644 --- a/bitbake/lib/toaster/orm/fixtures/oe-core.xml +++ b/bitbake/lib/toaster/orm/fixtures/oe-core.xml | |||
@@ -8,9 +8,9 @@ | |||
8 | 8 | ||
9 | <!-- Bitbake versions which correspond to the metadata release --> | 9 | <!-- Bitbake versions which correspond to the metadata release --> |
10 | <object model="orm.bitbakeversion" pk="1"> | 10 | <object model="orm.bitbakeversion" pk="1"> |
11 | <field type="CharField" name="name">kirkstone</field> | 11 | <field type="CharField" name="name">scarthgap</field> |
12 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> | 12 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> |
13 | <field type="CharField" name="branch">2.0</field> | 13 | <field type="CharField" name="branch">2.8</field> |
14 | </object> | 14 | </object> |
15 | <object model="orm.bitbakeversion" pk="2"> | 15 | <object model="orm.bitbakeversion" pk="2"> |
16 | <field type="CharField" name="name">HEAD</field> | 16 | <field type="CharField" name="name">HEAD</field> |
@@ -23,23 +23,33 @@ | |||
23 | <field type="CharField" name="branch">master</field> | 23 | <field type="CharField" name="branch">master</field> |
24 | </object> | 24 | </object> |
25 | <object model="orm.bitbakeversion" pk="4"> | 25 | <object model="orm.bitbakeversion" pk="4"> |
26 | <field type="CharField" name="name">mickledore</field> | 26 | <field type="CharField" name="name">whinlatter</field> |
27 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> | 27 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> |
28 | <field type="CharField" name="branch">2.4</field> | 28 | <field type="CharField" name="branch">2.14</field> |
29 | </object> | 29 | </object> |
30 | <object model="orm.bitbakeversion" pk="5"> | 30 | <object model="orm.bitbakeversion" pk="5"> |
31 | <field type="CharField" name="name">dunfell</field> | 31 | <field type="CharField" name="name">walnascar</field> |
32 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> | ||
33 | <field type="CharField" name="branch">2.12</field> | ||
34 | </object> | ||
35 | <object model="orm.bitbakeversion" pk="6"> | ||
36 | <field type="CharField" name="name">styhead</field> | ||
32 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> | 37 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> |
33 | <field type="CharField" name="branch">1.46</field> | 38 | <field type="CharField" name="branch">2.10</field> |
39 | </object> | ||
40 | <object model="orm.bitbakeversion" pk="7"> | ||
41 | <field type="CharField" name="name">kirkstone</field> | ||
42 | <field type="CharField" name="giturl">git://git.openembedded.org/bitbake</field> | ||
43 | <field type="CharField" name="branch">2.0</field> | ||
34 | </object> | 44 | </object> |
35 | 45 | ||
36 | <!-- Releases available --> | 46 | <!-- Releases available --> |
37 | <object model="orm.release" pk="1"> | 47 | <object model="orm.release" pk="1"> |
38 | <field type="CharField" name="name">kirkstone</field> | 48 | <field type="CharField" name="name">scarthgap</field> |
39 | <field type="CharField" name="description">Openembedded Kirkstone</field> | 49 | <field type="CharField" name="description">Openembedded Scarthgap</field> |
40 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">1</field> | 50 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">1</field> |
41 | <field type="CharField" name="branch_name">kirkstone</field> | 51 | <field type="CharField" name="branch_name">scarthgap</field> |
42 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=kirkstone\">OpenEmbedded Kirkstone</a> branch.</field> | 52 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=scarthgap\">OpenEmbedded Scarthgap</a> branch.</field> |
43 | </object> | 53 | </object> |
44 | <object model="orm.release" pk="2"> | 54 | <object model="orm.release" pk="2"> |
45 | <field type="CharField" name="name">local</field> | 55 | <field type="CharField" name="name">local</field> |
@@ -56,18 +66,32 @@ | |||
56 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/\">OpenEmbedded master</a> branch.</field> | 66 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/\">OpenEmbedded master</a> branch.</field> |
57 | </object> | 67 | </object> |
58 | <object model="orm.release" pk="4"> | 68 | <object model="orm.release" pk="4"> |
59 | <field type="CharField" name="name">mickledore</field> | 69 | <field type="CharField" name="name">whinlatter</field> |
60 | <field type="CharField" name="description">Openembedded Mickledore</field> | 70 | <field type="CharField" name="description">Openembedded Whinlatter</field> |
61 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">4</field> | 71 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">4</field> |
62 | <field type="CharField" name="branch_name">mickledore</field> | 72 | <field type="CharField" name="branch_name">whinlatter</field> |
63 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=mickledore\">OpenEmbedded Mickledore</a> branch.</field> | 73 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=whinlatter\">OpenEmbedded Whinlatter</a> branch.</field> |
64 | </object> | 74 | </object> |
65 | <object model="orm.release" pk="5"> | 75 | <object model="orm.release" pk="5"> |
66 | <field type="CharField" name="name">dunfell</field> | 76 | <field type="CharField" name="name">walnascar</field> |
67 | <field type="CharField" name="description">Openembedded Dunfell</field> | 77 | <field type="CharField" name="description">Openembedded Walnascar</field> |
68 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">5</field> | 78 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">5</field> |
69 | <field type="CharField" name="branch_name">dunfell</field> | 79 | <field type="CharField" name="branch_name">walnascar</field> |
70 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=dunfell\">OpenEmbedded Dunfell</a> branch.</field> | 80 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=walnascar\">OpenEmbedded Walnascar</a> branch.</field> |
81 | </object> | ||
82 | <object model="orm.release" pk="6"> | ||
83 | <field type="CharField" name="name">styhead</field> | ||
84 | <field type="CharField" name="description">Openembedded Styhead</field> | ||
85 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">6</field> | ||
86 | <field type="CharField" name="branch_name">styhead</field> | ||
87 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=styhead\">OpenEmbedded Styhead</a> branch.</field> | ||
88 | </object> | ||
89 | <object model="orm.release" pk="7"> | ||
90 | <field type="CharField" name="name">kirkstone</field> | ||
91 | <field type="CharField" name="description">Openembedded Kirkstone</field> | ||
92 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">7</field> | ||
93 | <field type="CharField" name="branch_name">kirkstone</field> | ||
94 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href=\"https://cgit.openembedded.org/openembedded-core/log/?h=kirkstone\">OpenEmbedded Kirkstone</a> branch.</field> | ||
71 | </object> | 95 | </object> |
72 | 96 | ||
73 | <!-- Default layers for each release --> | 97 | <!-- Default layers for each release --> |
@@ -91,6 +115,14 @@ | |||
91 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> | 115 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> |
92 | <field type="CharField" name="layer_name">openembedded-core</field> | 116 | <field type="CharField" name="layer_name">openembedded-core</field> |
93 | </object> | 117 | </object> |
118 | <object model="orm.releasedefaultlayer" pk="6"> | ||
119 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
120 | <field type="CharField" name="layer_name">openembedded-core</field> | ||
121 | </object> | ||
122 | <object model="orm.releasedefaultlayer" pk="7"> | ||
123 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
124 | <field type="CharField" name="layer_name">openembedded-core</field> | ||
125 | </object> | ||
94 | 126 | ||
95 | 127 | ||
96 | <!-- Layer for the Local release --> | 128 | <!-- Layer for the Local release --> |
diff --git a/bitbake/lib/toaster/orm/fixtures/poky.xml b/bitbake/lib/toaster/orm/fixtures/poky.xml index 121e52fd45..6cf4f0687a 100644 --- a/bitbake/lib/toaster/orm/fixtures/poky.xml +++ b/bitbake/lib/toaster/orm/fixtures/poky.xml | |||
@@ -8,9 +8,9 @@ | |||
8 | 8 | ||
9 | <!-- Bitbake versions which correspond to the metadata release --> | 9 | <!-- Bitbake versions which correspond to the metadata release --> |
10 | <object model="orm.bitbakeversion" pk="1"> | 10 | <object model="orm.bitbakeversion" pk="1"> |
11 | <field type="CharField" name="name">kirkstone</field> | 11 | <field type="CharField" name="name">scarthgap</field> |
12 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> | 12 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> |
13 | <field type="CharField" name="branch">kirkstone</field> | 13 | <field type="CharField" name="branch">scarthgap</field> |
14 | <field type="CharField" name="dirpath">bitbake</field> | 14 | <field type="CharField" name="dirpath">bitbake</field> |
15 | </object> | 15 | </object> |
16 | <object model="orm.bitbakeversion" pk="2"> | 16 | <object model="orm.bitbakeversion" pk="2"> |
@@ -26,26 +26,38 @@ | |||
26 | <field type="CharField" name="dirpath">bitbake</field> | 26 | <field type="CharField" name="dirpath">bitbake</field> |
27 | </object> | 27 | </object> |
28 | <object model="orm.bitbakeversion" pk="4"> | 28 | <object model="orm.bitbakeversion" pk="4"> |
29 | <field type="CharField" name="name">mickledore</field> | 29 | <field type="CharField" name="name">whinlatter</field> |
30 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> | 30 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> |
31 | <field type="CharField" name="branch">mickledore</field> | 31 | <field type="CharField" name="branch">whinlatter</field> |
32 | <field type="CharField" name="dirpath">bitbake</field> | 32 | <field type="CharField" name="dirpath">bitbake</field> |
33 | </object> | 33 | </object> |
34 | <object model="orm.bitbakeversion" pk="5"> | 34 | <object model="orm.bitbakeversion" pk="5"> |
35 | <field type="CharField" name="name">dunfell</field> | 35 | <field type="CharField" name="name">walnascar</field> |
36 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> | ||
37 | <field type="CharField" name="branch">walnascar</field> | ||
38 | <field type="CharField" name="dirpath">bitbake</field> | ||
39 | </object> | ||
40 | <object model="orm.bitbakeversion" pk="6"> | ||
41 | <field type="CharField" name="name">styhead</field> | ||
36 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> | 42 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> |
37 | <field type="CharField" name="branch">dunfell</field> | 43 | <field type="CharField" name="branch">styhead</field> |
44 | <field type="CharField" name="dirpath">bitbake</field> | ||
45 | </object> | ||
46 | <object model="orm.bitbakeversion" pk="7"> | ||
47 | <field type="CharField" name="name">kirkstone</field> | ||
48 | <field type="CharField" name="giturl">git://git.yoctoproject.org/poky</field> | ||
49 | <field type="CharField" name="branch">kirkstone</field> | ||
38 | <field type="CharField" name="dirpath">bitbake</field> | 50 | <field type="CharField" name="dirpath">bitbake</field> |
39 | </object> | 51 | </object> |
40 | 52 | ||
41 | 53 | ||
42 | <!-- Releases available --> | 54 | <!-- Releases available --> |
43 | <object model="orm.release" pk="1"> | 55 | <object model="orm.release" pk="1"> |
44 | <field type="CharField" name="name">kirkstone</field> | 56 | <field type="CharField" name="name">scarthgap</field> |
45 | <field type="CharField" name="description">Yocto Project 4.0 "Kirkstone"</field> | 57 | <field type="CharField" name="description">Yocto Project 5.0 "Scarthgap"</field> |
46 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">1</field> | 58 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">1</field> |
47 | <field type="CharField" name="branch_name">kirkstone</field> | 59 | <field type="CharField" name="branch_name">scarthgap</field> |
48 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=kirkstone">Yocto Project Kirkstone branch</a>.</field> | 60 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=scarthgap">Yocto Project Scarthgap branch</a>.</field> |
49 | </object> | 61 | </object> |
50 | <object model="orm.release" pk="2"> | 62 | <object model="orm.release" pk="2"> |
51 | <field type="CharField" name="name">local</field> | 63 | <field type="CharField" name="name">local</field> |
@@ -62,18 +74,32 @@ | |||
62 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/">Yocto Project Master branch</a>.</field> | 74 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/">Yocto Project Master branch</a>.</field> |
63 | </object> | 75 | </object> |
64 | <object model="orm.release" pk="4"> | 76 | <object model="orm.release" pk="4"> |
65 | <field type="CharField" name="name">mickledore</field> | 77 | <field type="CharField" name="name">whinlatter</field> |
66 | <field type="CharField" name="description">Yocto Project 4.2 "Mickledore"</field> | 78 | <field type="CharField" name="description">Yocto Project 5.3 "Whinlatter"</field> |
67 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">4</field> | 79 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">4</field> |
68 | <field type="CharField" name="branch_name">mickledore</field> | 80 | <field type="CharField" name="branch_name">whinlatter</field> |
69 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=mickledore">Yocto Project Mickledore branch</a>.</field> | 81 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=whinlatter">Yocto Project Whinlatter branch</a>.</field> |
70 | </object> | 82 | </object> |
71 | <object model="orm.release" pk="5"> | 83 | <object model="orm.release" pk="5"> |
72 | <field type="CharField" name="name">dunfell</field> | 84 | <field type="CharField" name="name">walnascar</field> |
73 | <field type="CharField" name="description">Yocto Project 3.1 "Dunfell"</field> | 85 | <field type="CharField" name="description">Yocto Project 5.2 "Walnascar"</field> |
74 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">5</field> | 86 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">5</field> |
75 | <field type="CharField" name="branch_name">dunfell</field> | 87 | <field type="CharField" name="branch_name">walnascar</field> |
76 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=dunfell">Yocto Project Dunfell branch</a>.</field> | 88 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=walnascar">Yocto Project Walnascar branch</a>.</field> |
89 | </object> | ||
90 | <object model="orm.release" pk="6"> | ||
91 | <field type="CharField" name="name">styhead</field> | ||
92 | <field type="CharField" name="description">Yocto Project 5.1 "Styhead"</field> | ||
93 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">6</field> | ||
94 | <field type="CharField" name="branch_name">styhead</field> | ||
95 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=styhead">Yocto Project Styhead branch</a>.</field> | ||
96 | </object> | ||
97 | <object model="orm.release" pk="7"> | ||
98 | <field type="CharField" name="name">kirkstone</field> | ||
99 | <field type="CharField" name="description">Yocto Project 4.0 "Kirkstone"</field> | ||
100 | <field rel="ManyToOneRel" to="orm.bitbakeversion" name="bitbake_version">7</field> | ||
101 | <field type="CharField" name="branch_name">kirkstone</field> | ||
102 | <field type="TextField" name="helptext">Toaster will run your builds using the tip of the <a href="https://git.yoctoproject.org/cgit/cgit.cgi/poky/log/?h=kirkstone">Yocto Project Kirkstone branch</a>.</field> | ||
77 | </object> | 103 | </object> |
78 | 104 | ||
79 | <!-- Default project layers for each release --> | 105 | <!-- Default project layers for each release --> |
@@ -137,6 +163,30 @@ | |||
137 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> | 163 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> |
138 | <field type="CharField" name="layer_name">meta-yocto-bsp</field> | 164 | <field type="CharField" name="layer_name">meta-yocto-bsp</field> |
139 | </object> | 165 | </object> |
166 | <object model="orm.releasedefaultlayer" pk="16"> | ||
167 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
168 | <field type="CharField" name="layer_name">openembedded-core</field> | ||
169 | </object> | ||
170 | <object model="orm.releasedefaultlayer" pk="17"> | ||
171 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
172 | <field type="CharField" name="layer_name">meta-poky</field> | ||
173 | </object> | ||
174 | <object model="orm.releasedefaultlayer" pk="18"> | ||
175 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
176 | <field type="CharField" name="layer_name">meta-yocto-bsp</field> | ||
177 | </object> | ||
178 | <object model="orm.releasedefaultlayer" pk="19"> | ||
179 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
180 | <field type="CharField" name="layer_name">openembedded-core</field> | ||
181 | </object> | ||
182 | <object model="orm.releasedefaultlayer" pk="20"> | ||
183 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
184 | <field type="CharField" name="layer_name">meta-poky</field> | ||
185 | </object> | ||
186 | <object model="orm.releasedefaultlayer" pk="21"> | ||
187 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
188 | <field type="CharField" name="layer_name">meta-yocto-bsp</field> | ||
189 | </object> | ||
140 | 190 | ||
141 | <!-- Default layers provided by poky | 191 | <!-- Default layers provided by poky |
142 | openembedded-core | 192 | openembedded-core |
@@ -155,7 +205,7 @@ | |||
155 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> | 205 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> |
156 | <field type="IntegerField" name="layer_source">0</field> | 206 | <field type="IntegerField" name="layer_source">0</field> |
157 | <field rel="ManyToOneRel" to="orm.release" name="release">1</field> | 207 | <field rel="ManyToOneRel" to="orm.release" name="release">1</field> |
158 | <field type="CharField" name="branch">kirkstone</field> | 208 | <field type="CharField" name="branch">scarthgap</field> |
159 | <field type="CharField" name="dirpath">meta</field> | 209 | <field type="CharField" name="dirpath">meta</field> |
160 | </object> | 210 | </object> |
161 | <object model="orm.layer_version" pk="2"> | 211 | <object model="orm.layer_version" pk="2"> |
@@ -177,14 +227,28 @@ | |||
177 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> | 227 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> |
178 | <field type="IntegerField" name="layer_source">0</field> | 228 | <field type="IntegerField" name="layer_source">0</field> |
179 | <field rel="ManyToOneRel" to="orm.release" name="release">4</field> | 229 | <field rel="ManyToOneRel" to="orm.release" name="release">4</field> |
180 | <field type="CharField" name="branch">mickledore</field> | 230 | <field type="CharField" name="branch">whinlatter</field> |
181 | <field type="CharField" name="dirpath">meta</field> | 231 | <field type="CharField" name="dirpath">meta</field> |
182 | </object> | 232 | </object> |
183 | <object model="orm.layer_version" pk="5"> | 233 | <object model="orm.layer_version" pk="5"> |
184 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> | 234 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> |
185 | <field type="IntegerField" name="layer_source">0</field> | 235 | <field type="IntegerField" name="layer_source">0</field> |
186 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> | 236 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> |
187 | <field type="CharField" name="branch">dunfell</field> | 237 | <field type="CharField" name="branch">walnascar</field> |
238 | <field type="CharField" name="dirpath">meta</field> | ||
239 | </object> | ||
240 | <object model="orm.layer_version" pk="6"> | ||
241 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> | ||
242 | <field type="IntegerField" name="layer_source">0</field> | ||
243 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
244 | <field type="CharField" name="branch">styhead</field> | ||
245 | <field type="CharField" name="dirpath">meta</field> | ||
246 | </object> | ||
247 | <object model="orm.layer_version" pk="7"> | ||
248 | <field rel="ManyToOneRel" to="orm.layer" name="layer">1</field> | ||
249 | <field type="IntegerField" name="layer_source">0</field> | ||
250 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
251 | <field type="CharField" name="branch">kirkstone</field> | ||
188 | <field type="CharField" name="dirpath">meta</field> | 252 | <field type="CharField" name="dirpath">meta</field> |
189 | </object> | 253 | </object> |
190 | 254 | ||
@@ -196,14 +260,14 @@ | |||
196 | <field type="CharField" name="vcs_web_tree_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> | 260 | <field type="CharField" name="vcs_web_tree_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> |
197 | <field type="CharField" name="vcs_web_file_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> | 261 | <field type="CharField" name="vcs_web_file_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> |
198 | </object> | 262 | </object> |
199 | <object model="orm.layer_version" pk="6"> | 263 | <object model="orm.layer_version" pk="8"> |
200 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | 264 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> |
201 | <field type="IntegerField" name="layer_source">0</field> | 265 | <field type="IntegerField" name="layer_source">0</field> |
202 | <field rel="ManyToOneRel" to="orm.release" name="release">1</field> | 266 | <field rel="ManyToOneRel" to="orm.release" name="release">1</field> |
203 | <field type="CharField" name="branch">kirkstone</field> | 267 | <field type="CharField" name="branch">scarthgap</field> |
204 | <field type="CharField" name="dirpath">meta-poky</field> | 268 | <field type="CharField" name="dirpath">meta-poky</field> |
205 | </object> | 269 | </object> |
206 | <object model="orm.layer_version" pk="7"> | 270 | <object model="orm.layer_version" pk="9"> |
207 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | 271 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> |
208 | <field type="IntegerField" name="layer_source">0</field> | 272 | <field type="IntegerField" name="layer_source">0</field> |
209 | <field rel="ManyToOneRel" to="orm.release" name="release">2</field> | 273 | <field rel="ManyToOneRel" to="orm.release" name="release">2</field> |
@@ -211,25 +275,39 @@ | |||
211 | <field type="CharField" name="commit">HEAD</field> | 275 | <field type="CharField" name="commit">HEAD</field> |
212 | <field type="CharField" name="dirpath">meta-poky</field> | 276 | <field type="CharField" name="dirpath">meta-poky</field> |
213 | </object> | 277 | </object> |
214 | <object model="orm.layer_version" pk="8"> | 278 | <object model="orm.layer_version" pk="10"> |
215 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | 279 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> |
216 | <field type="IntegerField" name="layer_source">0</field> | 280 | <field type="IntegerField" name="layer_source">0</field> |
217 | <field rel="ManyToOneRel" to="orm.release" name="release">3</field> | 281 | <field rel="ManyToOneRel" to="orm.release" name="release">3</field> |
218 | <field type="CharField" name="branch">master</field> | 282 | <field type="CharField" name="branch">master</field> |
219 | <field type="CharField" name="dirpath">meta-poky</field> | 283 | <field type="CharField" name="dirpath">meta-poky</field> |
220 | </object> | 284 | </object> |
221 | <object model="orm.layer_version" pk="9"> | 285 | <object model="orm.layer_version" pk="11"> |
222 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | 286 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> |
223 | <field type="IntegerField" name="layer_source">0</field> | 287 | <field type="IntegerField" name="layer_source">0</field> |
224 | <field rel="ManyToOneRel" to="orm.release" name="release">4</field> | 288 | <field rel="ManyToOneRel" to="orm.release" name="release">4</field> |
225 | <field type="CharField" name="branch">mickledore</field> | 289 | <field type="CharField" name="branch">whinlatter</field> |
226 | <field type="CharField" name="dirpath">meta-poky</field> | 290 | <field type="CharField" name="dirpath">meta-poky</field> |
227 | </object> | 291 | </object> |
228 | <object model="orm.layer_version" pk="10"> | 292 | <object model="orm.layer_version" pk="12"> |
229 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | 293 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> |
230 | <field type="IntegerField" name="layer_source">0</field> | 294 | <field type="IntegerField" name="layer_source">0</field> |
231 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> | 295 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> |
232 | <field type="CharField" name="branch">dunfell</field> | 296 | <field type="CharField" name="branch">walnascar</field> |
297 | <field type="CharField" name="dirpath">meta-poky</field> | ||
298 | </object> | ||
299 | <object model="orm.layer_version" pk="13"> | ||
300 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | ||
301 | <field type="IntegerField" name="layer_source">0</field> | ||
302 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
303 | <field type="CharField" name="branch">styhead</field> | ||
304 | <field type="CharField" name="dirpath">meta-poky</field> | ||
305 | </object> | ||
306 | <object model="orm.layer_version" pk="14"> | ||
307 | <field rel="ManyToOneRel" to="orm.layer" name="layer">2</field> | ||
308 | <field type="IntegerField" name="layer_source">0</field> | ||
309 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
310 | <field type="CharField" name="branch">kirkstone</field> | ||
233 | <field type="CharField" name="dirpath">meta-poky</field> | 311 | <field type="CharField" name="dirpath">meta-poky</field> |
234 | </object> | 312 | </object> |
235 | 313 | ||
@@ -241,14 +319,14 @@ | |||
241 | <field type="CharField" name="vcs_web_tree_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> | 319 | <field type="CharField" name="vcs_web_tree_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> |
242 | <field type="CharField" name="vcs_web_file_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> | 320 | <field type="CharField" name="vcs_web_file_base_url">https://git.yoctoproject.org/cgit/cgit.cgi/poky/tree/%path%?h=%branch%</field> |
243 | </object> | 321 | </object> |
244 | <object model="orm.layer_version" pk="11"> | 322 | <object model="orm.layer_version" pk="15"> |
245 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | 323 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> |
246 | <field type="IntegerField" name="layer_source">0</field> | 324 | <field type="IntegerField" name="layer_source">0</field> |
247 | <field rel="ManyToOneRel" to="orm.release" name="release">1</field> | 325 | <field rel="ManyToOneRel" to="orm.release" name="release">1</field> |
248 | <field type="CharField" name="branch">kirkstone</field> | 326 | <field type="CharField" name="branch">scarthgap</field> |
249 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | 327 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> |
250 | </object> | 328 | </object> |
251 | <object model="orm.layer_version" pk="12"> | 329 | <object model="orm.layer_version" pk="16"> |
252 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | 330 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> |
253 | <field type="IntegerField" name="layer_source">0</field> | 331 | <field type="IntegerField" name="layer_source">0</field> |
254 | <field rel="ManyToOneRel" to="orm.release" name="release">2</field> | 332 | <field rel="ManyToOneRel" to="orm.release" name="release">2</field> |
@@ -256,25 +334,39 @@ | |||
256 | <field type="CharField" name="commit">HEAD</field> | 334 | <field type="CharField" name="commit">HEAD</field> |
257 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | 335 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> |
258 | </object> | 336 | </object> |
259 | <object model="orm.layer_version" pk="13"> | 337 | <object model="orm.layer_version" pk="17"> |
260 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | 338 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> |
261 | <field type="IntegerField" name="layer_source">0</field> | 339 | <field type="IntegerField" name="layer_source">0</field> |
262 | <field rel="ManyToOneRel" to="orm.release" name="release">3</field> | 340 | <field rel="ManyToOneRel" to="orm.release" name="release">3</field> |
263 | <field type="CharField" name="branch">master</field> | 341 | <field type="CharField" name="branch">master</field> |
264 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | 342 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> |
265 | </object> | 343 | </object> |
266 | <object model="orm.layer_version" pk="14"> | 344 | <object model="orm.layer_version" pk="18"> |
267 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | 345 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> |
268 | <field type="IntegerField" name="layer_source">0</field> | 346 | <field type="IntegerField" name="layer_source">0</field> |
269 | <field rel="ManyToOneRel" to="orm.release" name="release">4</field> | 347 | <field rel="ManyToOneRel" to="orm.release" name="release">4</field> |
270 | <field type="CharField" name="branch">mickledore</field> | 348 | <field type="CharField" name="branch">whinlatter</field> |
271 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | 349 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> |
272 | </object> | 350 | </object> |
273 | <object model="orm.layer_version" pk="15"> | 351 | <object model="orm.layer_version" pk="19"> |
274 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | 352 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> |
275 | <field type="IntegerField" name="layer_source">0</field> | 353 | <field type="IntegerField" name="layer_source">0</field> |
276 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> | 354 | <field rel="ManyToOneRel" to="orm.release" name="release">5</field> |
277 | <field type="CharField" name="branch">dunfell</field> | 355 | <field type="CharField" name="branch">walnascar</field> |
356 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | ||
357 | </object> | ||
358 | <object model="orm.layer_version" pk="20"> | ||
359 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | ||
360 | <field type="IntegerField" name="layer_source">0</field> | ||
361 | <field rel="ManyToOneRel" to="orm.release" name="release">6</field> | ||
362 | <field type="CharField" name="branch">styhead</field> | ||
363 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | ||
364 | </object> | ||
365 | <object model="orm.layer_version" pk="21"> | ||
366 | <field rel="ManyToOneRel" to="orm.layer" name="layer">3</field> | ||
367 | <field type="IntegerField" name="layer_source">0</field> | ||
368 | <field rel="ManyToOneRel" to="orm.release" name="release">7</field> | ||
369 | <field type="CharField" name="branch">kirkstone</field> | ||
278 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> | 370 | <field type="CharField" name="dirpath">meta-yocto-bsp</field> |
279 | </object> | 371 | </object> |
280 | </django-objects> | 372 | </django-objects> |
diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py index 19c9686206..e2f488ed89 100644 --- a/bitbake/lib/toaster/orm/models.py +++ b/bitbake/lib/toaster/orm/models.py | |||
@@ -79,7 +79,6 @@ if 'sqlite' in settings.DATABASES['default']['ENGINE']: | |||
79 | # end of HACK | 79 | # end of HACK |
80 | 80 | ||
81 | class GitURLValidator(validators.URLValidator): | 81 | class GitURLValidator(validators.URLValidator): |
82 | import re | ||
83 | regex = re.compile( | 82 | regex = re.compile( |
84 | r'^(?:ssh|git|http|ftp)s?://' # http:// or https:// | 83 | r'^(?:ssh|git|http|ftp)s?://' # http:// or https:// |
85 | r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... | 84 | r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... |
@@ -1500,7 +1499,7 @@ class Layer_Version(models.Model): | |||
1500 | # code lifted, with adaptations, from the layerindex-web application | 1499 | # code lifted, with adaptations, from the layerindex-web application |
1501 | # https://git.yoctoproject.org/cgit/cgit.cgi/layerindex-web/ | 1500 | # https://git.yoctoproject.org/cgit/cgit.cgi/layerindex-web/ |
1502 | def _handle_url_path(self, base_url, path): | 1501 | def _handle_url_path(self, base_url, path): |
1503 | import re, posixpath | 1502 | import posixpath |
1504 | if base_url: | 1503 | if base_url: |
1505 | if self.dirpath: | 1504 | if self.dirpath: |
1506 | if path: | 1505 | if path: |
diff --git a/bitbake/lib/toaster/tests/browser/selenium_helpers_base.py b/bitbake/lib/toaster/tests/browser/selenium_helpers_base.py index 393be75496..6953541ab5 100644 --- a/bitbake/lib/toaster/tests/browser/selenium_helpers_base.py +++ b/bitbake/lib/toaster/tests/browser/selenium_helpers_base.py | |||
@@ -27,7 +27,7 @@ from selenium.webdriver.common.by import By | |||
27 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities | 27 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities |
28 | from selenium.common.exceptions import NoSuchElementException, \ | 28 | from selenium.common.exceptions import NoSuchElementException, \ |
29 | StaleElementReferenceException, TimeoutException, \ | 29 | StaleElementReferenceException, TimeoutException, \ |
30 | SessionNotCreatedException | 30 | SessionNotCreatedException, WebDriverException |
31 | 31 | ||
32 | def create_selenium_driver(cls,browser='chrome'): | 32 | def create_selenium_driver(cls,browser='chrome'): |
33 | # set default browser string based on env (if available) | 33 | # set default browser string based on env (if available) |
@@ -90,7 +90,7 @@ class Wait(WebDriverWait): | |||
90 | Subclass of WebDriverWait with predetermined timeout and poll | 90 | Subclass of WebDriverWait with predetermined timeout and poll |
91 | frequency. Also deals with a wider variety of exceptions. | 91 | frequency. Also deals with a wider variety of exceptions. |
92 | """ | 92 | """ |
93 | _TIMEOUT = 10 | 93 | _TIMEOUT = 20 |
94 | _POLL_FREQUENCY = 0.5 | 94 | _POLL_FREQUENCY = 0.5 |
95 | 95 | ||
96 | def __init__(self, driver, timeout=_TIMEOUT, poll=_POLL_FREQUENCY): | 96 | def __init__(self, driver, timeout=_TIMEOUT, poll=_POLL_FREQUENCY): |
@@ -114,6 +114,9 @@ class Wait(WebDriverWait): | |||
114 | pass | 114 | pass |
115 | except StaleElementReferenceException: | 115 | except StaleElementReferenceException: |
116 | pass | 116 | pass |
117 | except WebDriverException: | ||
118 | # selenium.common.exceptions.WebDriverException: Message: unknown error: unhandled inspector error: {"code":-32000,"message":"Node with given id does not belong to the document"} | ||
119 | pass | ||
117 | 120 | ||
118 | time.sleep(self._poll) | 121 | time.sleep(self._poll) |
119 | if time.time() > end_time: | 122 | if time.time() > end_time: |
@@ -183,7 +186,7 @@ class SeleniumTestCaseBase(unittest.TestCase): | |||
183 | self.driver.get(abs_url) | 186 | self.driver.get(abs_url) |
184 | 187 | ||
185 | try: # Ensure page is loaded before proceeding | 188 | try: # Ensure page is loaded before proceeding |
186 | self.wait_until_visible("#global-nav", poll=3) | 189 | self.wait_until_visible("#global-nav") |
187 | except NoSuchElementException: | 190 | except NoSuchElementException: |
188 | self.driver.implicitly_wait(3) | 191 | self.driver.implicitly_wait(3) |
189 | except TimeoutException: | 192 | except TimeoutException: |
@@ -208,36 +211,43 @@ class SeleniumTestCaseBase(unittest.TestCase): | |||
208 | """ Return the element which currently has focus on the page """ | 211 | """ Return the element which currently has focus on the page """ |
209 | return self.driver.switch_to.active_element | 212 | return self.driver.switch_to.active_element |
210 | 213 | ||
211 | def wait_until_present(self, selector, poll=0.5): | 214 | def wait_until_present(self, selector, timeout=Wait._TIMEOUT): |
212 | """ Wait until element matching CSS selector is on the page """ | 215 | """ Wait until element matching CSS selector is on the page """ |
213 | is_present = lambda driver: self.find(selector) | 216 | is_present = lambda driver: self.find(selector) |
214 | msg = 'An element matching "%s" should be on the page' % selector | 217 | msg = 'An element matching "%s" should be on the page' % selector |
215 | element = Wait(self.driver, poll=poll).until(is_present, msg) | 218 | element = Wait(self.driver, timeout=timeout).until(is_present, msg) |
216 | if poll > 2: | ||
217 | time.sleep(poll) # element need more delay to be present | ||
218 | return element | 219 | return element |
219 | 220 | ||
220 | def wait_until_visible(self, selector, poll=1): | 221 | def wait_until_visible(self, selector, timeout=Wait._TIMEOUT): |
221 | """ Wait until element matching CSS selector is visible on the page """ | 222 | """ Wait until element matching CSS selector is visible on the page """ |
222 | is_visible = lambda driver: self.find(selector).is_displayed() | 223 | is_visible = lambda driver: self.find(selector).is_displayed() |
223 | msg = 'An element matching "%s" should be visible' % selector | 224 | msg = 'An element matching "%s" should be visible' % selector |
224 | Wait(self.driver, poll=poll).until(is_visible, msg) | 225 | Wait(self.driver, timeout=timeout).until(is_visible, msg) |
225 | time.sleep(poll) # wait for visibility to settle | 226 | return self.find(selector) |
227 | |||
228 | def wait_until_not_visible(self, selector, timeout=Wait._TIMEOUT): | ||
229 | """ Wait until element matching CSS selector is not visible on the page """ | ||
230 | is_visible = lambda driver: self.find(selector).is_displayed() | ||
231 | msg = 'An element matching "%s" should be visible' % selector | ||
232 | Wait(self.driver, timeout=timeout).until_not(is_visible, msg) | ||
226 | return self.find(selector) | 233 | return self.find(selector) |
227 | 234 | ||
228 | def wait_until_clickable(self, selector, poll=1): | 235 | def wait_until_clickable(self, selector, timeout=Wait._TIMEOUT): |
229 | """ Wait until element matching CSS selector is visible on the page """ | 236 | """ Wait until element matching CSS selector is visible on the page """ |
230 | WebDriverWait( | 237 | WebDriverWait(self.driver, timeout=timeout).until(lambda driver: self.driver.execute_script("return jQuery.active == 0")) |
231 | self.driver, | 238 | is_clickable = lambda driver: (self.find(selector).is_displayed() and self.find(selector).is_enabled()) |
232 | Wait._TIMEOUT, | 239 | msg = 'An element matching "%s" should be clickable' % selector |
233 | poll_frequency=poll | 240 | Wait(self.driver, timeout=timeout).until(is_clickable, msg) |
234 | ).until( | ||
235 | EC.element_to_be_clickable((By.ID, selector.removeprefix('#') | ||
236 | ) | ||
237 | ) | ||
238 | ) | ||
239 | return self.find(selector) | 241 | return self.find(selector) |
240 | 242 | ||
243 | def wait_until_element_clickable(self, finder, timeout=Wait._TIMEOUT): | ||
244 | """ Wait until element is clickable """ | ||
245 | WebDriverWait(self.driver, timeout=timeout).until(lambda driver: self.driver.execute_script("return jQuery.active == 0")) | ||
246 | is_clickable = lambda driver: (finder(driver).is_displayed() and finder(driver).is_enabled()) | ||
247 | msg = 'A matching element never became be clickable' | ||
248 | Wait(self.driver, timeout=timeout).until(is_clickable, msg) | ||
249 | return finder(self.driver) | ||
250 | |||
241 | def wait_until_focused(self, selector): | 251 | def wait_until_focused(self, selector): |
242 | """ Wait until element matching CSS selector has focus """ | 252 | """ Wait until element matching CSS selector has focus """ |
243 | is_focused = \ | 253 | is_focused = \ |
diff --git a/bitbake/lib/toaster/tests/browser/test_all_builds_page.py b/bitbake/lib/toaster/tests/browser/test_all_builds_page.py index b9356a0344..9ab81fb11b 100644 --- a/bitbake/lib/toaster/tests/browser/test_all_builds_page.py +++ b/bitbake/lib/toaster/tests/browser/test_all_builds_page.py | |||
@@ -200,6 +200,7 @@ class TestAllBuildsPage(SeleniumTestCase): | |||
200 | 200 | ||
201 | # should see a rebuild button for non-command-line builds | 201 | # should see a rebuild button for non-command-line builds |
202 | self.wait_until_visible('#allbuildstable tbody tr') | 202 | self.wait_until_visible('#allbuildstable tbody tr') |
203 | self.wait_until_visible('.rebuild-btn') | ||
203 | selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % build1.id | 204 | selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % build1.id |
204 | run_again_button = self.find_all(selector) | 205 | run_again_button = self.find_all(selector) |
205 | self.assertEqual(len(run_again_button), 1, | 206 | self.assertEqual(len(run_again_button), 1, |
@@ -224,7 +225,7 @@ class TestAllBuildsPage(SeleniumTestCase): | |||
224 | 225 | ||
225 | url = reverse('all-builds') | 226 | url = reverse('all-builds') |
226 | self.get(url) | 227 | self.get(url) |
227 | self.wait_until_visible('#allbuildstable', poll=3) | 228 | self.wait_until_visible('#allbuildstable') |
228 | 229 | ||
229 | # get the project name cells from the table | 230 | # get the project name cells from the table |
230 | cells = self.find_all('#allbuildstable td[class="project"]') | 231 | cells = self.find_all('#allbuildstable td[class="project"]') |
@@ -257,7 +258,7 @@ class TestAllBuildsPage(SeleniumTestCase): | |||
257 | 258 | ||
258 | url = reverse('all-builds') | 259 | url = reverse('all-builds') |
259 | self.get(url) | 260 | self.get(url) |
260 | self.wait_until_visible('#allbuildstable', poll=3) | 261 | self.wait_until_visible('#allbuildstable') |
261 | 262 | ||
262 | # test recent builds area for successful build | 263 | # test recent builds area for successful build |
263 | element = self._get_build_time_element(build1) | 264 | element = self._get_build_time_element(build1) |
@@ -452,7 +453,7 @@ class TestAllBuildsPage(SeleniumTestCase): | |||
452 | def test_show_rows(row_to_show, show_row_link): | 453 | def test_show_rows(row_to_show, show_row_link): |
453 | # Check that we can show rows == row_to_show | 454 | # Check that we can show rows == row_to_show |
454 | show_row_link.select_by_value(str(row_to_show)) | 455 | show_row_link.select_by_value(str(row_to_show)) |
455 | self.wait_until_visible('#allbuildstable tbody tr', poll=3) | 456 | self.wait_until_visible('#allbuildstable tbody tr') |
456 | # check at least some rows are visible | 457 | # check at least some rows are visible |
457 | self.assertTrue( | 458 | self.assertTrue( |
458 | len(self.find_all('#allbuildstable tbody tr')) > 0 | 459 | len(self.find_all('#allbuildstable tbody tr')) > 0 |
diff --git a/bitbake/lib/toaster/tests/browser/test_all_projects_page.py b/bitbake/lib/toaster/tests/browser/test_all_projects_page.py index 9ed1901cc9..05e12892be 100644 --- a/bitbake/lib/toaster/tests/browser/test_all_projects_page.py +++ b/bitbake/lib/toaster/tests/browser/test_all_projects_page.py | |||
@@ -81,7 +81,7 @@ class TestAllProjectsPage(SeleniumTestCase): | |||
81 | 81 | ||
82 | def _get_row_for_project(self, project_name): | 82 | def _get_row_for_project(self, project_name): |
83 | """ Get the HTML row for a project, or None if not found """ | 83 | """ Get the HTML row for a project, or None if not found """ |
84 | self.wait_until_visible('#projectstable tbody tr', poll=3) | 84 | self.wait_until_visible('#projectstable tbody tr') |
85 | rows = self.find_all('#projectstable tbody tr') | 85 | rows = self.find_all('#projectstable tbody tr') |
86 | 86 | ||
87 | # find the row with a project name matching the one supplied | 87 | # find the row with a project name matching the one supplied |
@@ -236,7 +236,7 @@ class TestAllProjectsPage(SeleniumTestCase): | |||
236 | self.get(url) | 236 | self.get(url) |
237 | 237 | ||
238 | # Chseck search box is present and works | 238 | # Chseck search box is present and works |
239 | self.wait_until_visible('#projectstable tbody tr', poll=3) | 239 | self.wait_until_visible('#projectstable tbody tr') |
240 | search_box = self.find('#search-input-projectstable') | 240 | search_box = self.find('#search-input-projectstable') |
241 | self.assertTrue(search_box.is_displayed()) | 241 | self.assertTrue(search_box.is_displayed()) |
242 | 242 | ||
@@ -244,7 +244,7 @@ class TestAllProjectsPage(SeleniumTestCase): | |||
244 | search_box.send_keys('test project 10') | 244 | search_box.send_keys('test project 10') |
245 | search_btn = self.find('#search-submit-projectstable') | 245 | search_btn = self.find('#search-submit-projectstable') |
246 | search_btn.click() | 246 | search_btn.click() |
247 | self.wait_until_visible('#projectstable tbody tr', poll=3) | 247 | self.wait_until_visible('#projectstable tbody tr') |
248 | rows = self.find_all('#projectstable tbody tr') | 248 | rows = self.find_all('#projectstable tbody tr') |
249 | self.assertTrue(len(rows) == 1) | 249 | self.assertTrue(len(rows) == 1) |
250 | 250 | ||
@@ -290,7 +290,7 @@ class TestAllProjectsPage(SeleniumTestCase): | |||
290 | ) | 290 | ) |
291 | url = reverse('all-projects') | 291 | url = reverse('all-projects') |
292 | self.get(url) | 292 | self.get(url) |
293 | self.wait_until_visible('#projectstable tbody tr', poll=3) | 293 | self.wait_until_visible('#projectstable tbody tr') |
294 | 294 | ||
295 | # Check edit column | 295 | # Check edit column |
296 | edit_column = self.find('#edit-columns-button') | 296 | edit_column = self.find('#edit-columns-button') |
@@ -313,7 +313,7 @@ class TestAllProjectsPage(SeleniumTestCase): | |||
313 | def test_show_rows(row_to_show, show_row_link): | 313 | def test_show_rows(row_to_show, show_row_link): |
314 | # Check that we can show rows == row_to_show | 314 | # Check that we can show rows == row_to_show |
315 | show_row_link.select_by_value(str(row_to_show)) | 315 | show_row_link.select_by_value(str(row_to_show)) |
316 | self.wait_until_visible('#projectstable tbody tr', poll=3) | 316 | self.wait_until_visible('#projectstable tbody tr') |
317 | # check at least some rows are visible | 317 | # check at least some rows are visible |
318 | self.assertTrue( | 318 | self.assertTrue( |
319 | len(self.find_all('#projectstable tbody tr')) > 0 | 319 | len(self.find_all('#projectstable tbody tr')) > 0 |
@@ -321,7 +321,7 @@ class TestAllProjectsPage(SeleniumTestCase): | |||
321 | 321 | ||
322 | url = reverse('all-projects') | 322 | url = reverse('all-projects') |
323 | self.get(url) | 323 | self.get(url) |
324 | self.wait_until_visible('#projectstable tbody tr', poll=3) | 324 | self.wait_until_visible('#projectstable tbody tr') |
325 | 325 | ||
326 | show_rows = self.driver.find_elements( | 326 | show_rows = self.driver.find_elements( |
327 | By.XPATH, | 327 | By.XPATH, |
diff --git a/bitbake/lib/toaster/tests/browser/test_builddashboard_page.py b/bitbake/lib/toaster/tests/browser/test_builddashboard_page.py index d838ce363a..82367108e2 100644 --- a/bitbake/lib/toaster/tests/browser/test_builddashboard_page.py +++ b/bitbake/lib/toaster/tests/browser/test_builddashboard_page.py | |||
@@ -162,7 +162,7 @@ class TestBuildDashboardPage(SeleniumTestCase): | |||
162 | """ | 162 | """ |
163 | url = reverse('builddashboard', args=(build.id,)) | 163 | url = reverse('builddashboard', args=(build.id,)) |
164 | self.get(url) | 164 | self.get(url) |
165 | self.wait_until_visible('#global-nav', poll=3) | 165 | self.wait_until_visible('#global-nav') |
166 | 166 | ||
167 | def _get_build_dashboard_errors(self, build): | 167 | def _get_build_dashboard_errors(self, build): |
168 | """ | 168 | """ |
diff --git a/bitbake/lib/toaster/tests/browser/test_landing_page.py b/bitbake/lib/toaster/tests/browser/test_landing_page.py index 8fe5fea467..210359d561 100644 --- a/bitbake/lib/toaster/tests/browser/test_landing_page.py +++ b/bitbake/lib/toaster/tests/browser/test_landing_page.py | |||
@@ -34,6 +34,7 @@ class TestLandingPage(SeleniumTestCase): | |||
34 | def test_icon_info_visible_and_clickable(self): | 34 | def test_icon_info_visible_and_clickable(self): |
35 | """ Test that the information icon is visible and clickable """ | 35 | """ Test that the information icon is visible and clickable """ |
36 | self.get(reverse('landing')) | 36 | self.get(reverse('landing')) |
37 | self.wait_until_visible('#toaster-version-info-sign') | ||
37 | info_sign = self.find('#toaster-version-info-sign') | 38 | info_sign = self.find('#toaster-version-info-sign') |
38 | 39 | ||
39 | # check that the info sign is visible | 40 | # check that the info sign is visible |
@@ -43,6 +44,7 @@ class TestLandingPage(SeleniumTestCase): | |||
43 | # and info modal is appearing when clicking on the info sign | 44 | # and info modal is appearing when clicking on the info sign |
44 | info_sign.click() # click on the info sign make attribute 'aria-describedby' visible | 45 | info_sign.click() # click on the info sign make attribute 'aria-describedby' visible |
45 | info_model_id = info_sign.get_attribute('aria-describedby') | 46 | info_model_id = info_sign.get_attribute('aria-describedby') |
47 | self.wait_until_visible(f'#{info_model_id}') | ||
46 | info_modal = self.find(f'#{info_model_id}') | 48 | info_modal = self.find(f'#{info_model_id}') |
47 | self.assertTrue(info_modal.is_displayed()) | 49 | self.assertTrue(info_modal.is_displayed()) |
48 | self.assertTrue("Toaster version information" in info_modal.text) | 50 | self.assertTrue("Toaster version information" in info_modal.text) |
@@ -50,6 +52,7 @@ class TestLandingPage(SeleniumTestCase): | |||
50 | def test_documentation_link_displayed(self): | 52 | def test_documentation_link_displayed(self): |
51 | """ Test that the documentation link is displayed """ | 53 | """ Test that the documentation link is displayed """ |
52 | self.get(reverse('landing')) | 54 | self.get(reverse('landing')) |
55 | self.wait_until_visible('#navbar-docs') | ||
53 | documentation_link = self.find('#navbar-docs > a') | 56 | documentation_link = self.find('#navbar-docs > a') |
54 | 57 | ||
55 | # check that the documentation link is visible | 58 | # check that the documentation link is visible |
@@ -65,6 +68,7 @@ class TestLandingPage(SeleniumTestCase): | |||
65 | def test_openembedded_jumbotron_link_visible_and_clickable(self): | 68 | def test_openembedded_jumbotron_link_visible_and_clickable(self): |
66 | """ Test OpenEmbedded link jumbotron is visible and clickable: """ | 69 | """ Test OpenEmbedded link jumbotron is visible and clickable: """ |
67 | self.get(reverse('landing')) | 70 | self.get(reverse('landing')) |
71 | self.wait_until_visible('.jumbotron') | ||
68 | jumbotron = self.find('.jumbotron') | 72 | jumbotron = self.find('.jumbotron') |
69 | 73 | ||
70 | # check OpenEmbedded | 74 | # check OpenEmbedded |
@@ -76,6 +80,7 @@ class TestLandingPage(SeleniumTestCase): | |||
76 | def test_bitbake_jumbotron_link_visible_and_clickable(self): | 80 | def test_bitbake_jumbotron_link_visible_and_clickable(self): |
77 | """ Test BitBake link jumbotron is visible and clickable: """ | 81 | """ Test BitBake link jumbotron is visible and clickable: """ |
78 | self.get(reverse('landing')) | 82 | self.get(reverse('landing')) |
83 | self.wait_until_visible('.jumbotron') | ||
79 | jumbotron = self.find('.jumbotron') | 84 | jumbotron = self.find('.jumbotron') |
80 | 85 | ||
81 | # check BitBake | 86 | # check BitBake |
@@ -88,6 +93,7 @@ class TestLandingPage(SeleniumTestCase): | |||
88 | def test_yoctoproject_jumbotron_link_visible_and_clickable(self): | 93 | def test_yoctoproject_jumbotron_link_visible_and_clickable(self): |
89 | """ Test Yocto Project link jumbotron is visible and clickable: """ | 94 | """ Test Yocto Project link jumbotron is visible and clickable: """ |
90 | self.get(reverse('landing')) | 95 | self.get(reverse('landing')) |
96 | self.wait_until_visible('.jumbotron') | ||
91 | jumbotron = self.find('.jumbotron') | 97 | jumbotron = self.find('.jumbotron') |
92 | 98 | ||
93 | # check Yocto Project | 99 | # check Yocto Project |
@@ -101,6 +107,7 @@ class TestLandingPage(SeleniumTestCase): | |||
101 | if visible and clickable | 107 | if visible and clickable |
102 | """ | 108 | """ |
103 | self.get(reverse('landing')) | 109 | self.get(reverse('landing')) |
110 | self.wait_until_visible('.jumbotron') | ||
104 | jumbotron = self.find('.jumbotron') | 111 | jumbotron = self.find('.jumbotron') |
105 | 112 | ||
106 | # check Big magenta button | 113 | # check Big magenta button |
@@ -119,6 +126,7 @@ class TestLandingPage(SeleniumTestCase): | |||
119 | Layer_Version.objects.create(layer=layer) | 126 | Layer_Version.objects.create(layer=layer) |
120 | 127 | ||
121 | self.get(reverse('landing')) | 128 | self.get(reverse('landing')) |
129 | self.wait_until_visible('.jumbotron') | ||
122 | jumbotron = self.find('.jumbotron') | 130 | jumbotron = self.find('.jumbotron') |
123 | 131 | ||
124 | # check Big Blue button | 132 | # check Big Blue button |
@@ -132,6 +140,7 @@ class TestLandingPage(SeleniumTestCase): | |||
132 | def test_toaster_manual_link_visible_and_clickable(self): | 140 | def test_toaster_manual_link_visible_and_clickable(self): |
133 | """ Test Read the Toaster manual link jumbotron is visible and clickable: """ | 141 | """ Test Read the Toaster manual link jumbotron is visible and clickable: """ |
134 | self.get(reverse('landing')) | 142 | self.get(reverse('landing')) |
143 | self.wait_until_visible('.jumbotron') | ||
135 | jumbotron = self.find('.jumbotron') | 144 | jumbotron = self.find('.jumbotron') |
136 | 145 | ||
137 | # check Read the Toaster manual | 146 | # check Read the Toaster manual |
@@ -145,6 +154,7 @@ class TestLandingPage(SeleniumTestCase): | |||
145 | def test_contrib_to_toaster_link_visible_and_clickable(self): | 154 | def test_contrib_to_toaster_link_visible_and_clickable(self): |
146 | """ Test Contribute to Toaster link jumbotron is visible and clickable: """ | 155 | """ Test Contribute to Toaster link jumbotron is visible and clickable: """ |
147 | self.get(reverse('landing')) | 156 | self.get(reverse('landing')) |
157 | self.wait_until_visible('.jumbotron') | ||
148 | jumbotron = self.find('.jumbotron') | 158 | jumbotron = self.find('.jumbotron') |
149 | 159 | ||
150 | # check Contribute to Toaster | 160 | # check Contribute to Toaster |
@@ -161,6 +171,7 @@ class TestLandingPage(SeleniumTestCase): | |||
161 | => should see the landing page | 171 | => should see the landing page |
162 | """ | 172 | """ |
163 | self.get(reverse('landing')) | 173 | self.get(reverse('landing')) |
174 | self.wait_until_visible('.jumbotron') | ||
164 | self.assertTrue(self.LANDING_PAGE_TITLE in self.get_page_source()) | 175 | self.assertTrue(self.LANDING_PAGE_TITLE in self.get_page_source()) |
165 | 176 | ||
166 | def test_default_project_has_build(self): | 177 | def test_default_project_has_build(self): |
@@ -193,6 +204,7 @@ class TestLandingPage(SeleniumTestCase): | |||
193 | user_project.save() | 204 | user_project.save() |
194 | 205 | ||
195 | self.get(reverse('landing')) | 206 | self.get(reverse('landing')) |
207 | self.wait_until_visible('#projectstable') | ||
196 | 208 | ||
197 | elements = self.find_all('#projectstable') | 209 | elements = self.find_all('#projectstable') |
198 | self.assertEqual(len(elements), 1, 'should redirect to projects') | 210 | self.assertEqual(len(elements), 1, 'should redirect to projects') |
@@ -213,7 +225,7 @@ class TestLandingPage(SeleniumTestCase): | |||
213 | 225 | ||
214 | self.get(reverse('landing')) | 226 | self.get(reverse('landing')) |
215 | 227 | ||
216 | self.wait_until_visible("#latest-builds", poll=3) | 228 | self.wait_until_visible("#latest-builds") |
217 | elements = self.find_all('#allbuildstable') | 229 | elements = self.find_all('#allbuildstable') |
218 | self.assertEqual(len(elements), 1, 'should redirect to builds') | 230 | self.assertEqual(len(elements), 1, 'should redirect to builds') |
219 | content = self.get_page_source() | 231 | content = self.get_page_source() |
diff --git a/bitbake/lib/toaster/tests/browser/test_layerdetails_page.py b/bitbake/lib/toaster/tests/browser/test_layerdetails_page.py index 5c29548b78..6abfdef699 100644 --- a/bitbake/lib/toaster/tests/browser/test_layerdetails_page.py +++ b/bitbake/lib/toaster/tests/browser/test_layerdetails_page.py | |||
@@ -64,7 +64,7 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
64 | args=(self.project.pk, | 64 | args=(self.project.pk, |
65 | self.imported_layer_version.pk)) | 65 | self.imported_layer_version.pk)) |
66 | 66 | ||
67 | def _edit_layerdetails(self): | 67 | def test_edit_layerdetails_page(self): |
68 | """ Edit all the editable fields for the layer refresh the page and | 68 | """ Edit all the editable fields for the layer refresh the page and |
69 | check that the new values exist""" | 69 | check that the new values exist""" |
70 | 70 | ||
@@ -100,24 +100,19 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
100 | (self.initial_values, value)) | 100 | (self.initial_values, value)) |
101 | 101 | ||
102 | # Make sure the input visible beofre sending keys | 102 | # Make sure the input visible beofre sending keys |
103 | self.wait_until_visible("#layer-git input[type=text]") | 103 | self.wait_until_clickable("#layer-git input[type=text]") |
104 | inputs.send_keys("-edited") | 104 | inputs.send_keys("-edited") |
105 | 105 | ||
106 | # Save the new values | 106 | # Save the new values |
107 | for save_btn in self.find_all(".change-btn"): | 107 | for save_btn in self.find_all(".change-btn"): |
108 | save_btn.click() | 108 | save_btn.click() |
109 | 109 | ||
110 | try: | 110 | self.wait_until_visible("#save-changes-for-switch") |
111 | self.wait_until_visible("#save-changes-for-switch", poll=3) | 111 | # Ensure scrolled into view |
112 | btn_save_chg_for_switch = self.wait_until_clickable( | 112 | self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})') |
113 | "#save-changes-for-switch", poll=3) | 113 | btn_save_chg_for_switch = self.wait_until_clickable( |
114 | btn_save_chg_for_switch.click() | 114 | "#save-changes-for-switch") |
115 | except ElementClickInterceptedException: | 115 | btn_save_chg_for_switch.click() |
116 | self.skipTest( | ||
117 | "save-changes-for-switch click intercepted. Element not visible or maybe covered by another element.") | ||
118 | except TimeoutException: | ||
119 | self.skipTest( | ||
120 | "save-changes-for-switch is not clickable within the specified timeout.") | ||
121 | 116 | ||
122 | self.wait_until_visible("#edit-layer-source") | 117 | self.wait_until_visible("#edit-layer-source") |
123 | 118 | ||
@@ -147,17 +142,10 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
147 | new_dir = "/home/test/my-meta-dir" | 142 | new_dir = "/home/test/my-meta-dir" |
148 | dir_input.send_keys(new_dir) | 143 | dir_input.send_keys(new_dir) |
149 | 144 | ||
150 | try: | 145 | self.wait_until_visible("#save-changes-for-switch") |
151 | self.wait_until_visible("#save-changes-for-switch", poll=3) | 146 | btn_save_chg_for_switch = self.wait_until_clickable( |
152 | btn_save_chg_for_switch = self.wait_until_clickable( | 147 | "#save-changes-for-switch") |
153 | "#save-changes-for-switch", poll=3) | 148 | btn_save_chg_for_switch.click() |
154 | btn_save_chg_for_switch.click() | ||
155 | except ElementClickInterceptedException: | ||
156 | self.skipTest( | ||
157 | "save-changes-for-switch click intercepted. Element not properly visible or maybe behind another element.") | ||
158 | except TimeoutException: | ||
159 | self.skipTest( | ||
160 | "save-changes-for-switch is not clickable within the specified timeout.") | ||
161 | 149 | ||
162 | self.wait_until_visible("#edit-layer-source") | 150 | self.wait_until_visible("#edit-layer-source") |
163 | 151 | ||
@@ -168,12 +156,6 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
168 | "Expected %s in the dir value for layer directory" % | 156 | "Expected %s in the dir value for layer directory" % |
169 | new_dir) | 157 | new_dir) |
170 | 158 | ||
171 | def test_edit_layerdetails_page(self): | ||
172 | try: | ||
173 | self._edit_layerdetails() | ||
174 | except ElementClickInterceptedException: | ||
175 | self.skipTest( | ||
176 | "ElementClickInterceptedException occured. Element not visible or maybe covered by another element.") | ||
177 | 159 | ||
178 | def test_delete_layer(self): | 160 | def test_delete_layer(self): |
179 | """ Delete the layer """ | 161 | """ Delete the layer """ |
@@ -211,6 +193,7 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
211 | self.get(self.url) | 193 | self.get(self.url) |
212 | 194 | ||
213 | # Add the layer | 195 | # Add the layer |
196 | self.wait_until_clickable("#add-remove-layer-btn") | ||
214 | self.click("#add-remove-layer-btn") | 197 | self.click("#add-remove-layer-btn") |
215 | 198 | ||
216 | notification = self.wait_until_visible("#change-notification-msg") | 199 | notification = self.wait_until_visible("#change-notification-msg") |
@@ -218,12 +201,17 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
218 | expected_text = "You have added 1 layer to your project: %s" % \ | 201 | expected_text = "You have added 1 layer to your project: %s" % \ |
219 | self.imported_layer_version.layer.name | 202 | self.imported_layer_version.layer.name |
220 | 203 | ||
221 | self.assertTrue(expected_text in notification.text, | 204 | self.assertIn(expected_text, notification.text, |
222 | "Expected notification text %s not found was " | 205 | "Expected notification text %s not found was " |
223 | " \"%s\" instead" % | 206 | " \"%s\" instead" % |
224 | (expected_text, notification.text)) | 207 | (expected_text, notification.text)) |
225 | 208 | ||
209 | hide_button = self.find('#hide-alert') | ||
210 | hide_button.click() | ||
211 | self.wait_until_not_visible('#change-notification') | ||
212 | |||
226 | # Remove the layer | 213 | # Remove the layer |
214 | self.wait_until_clickable("#add-remove-layer-btn") | ||
227 | self.click("#add-remove-layer-btn") | 215 | self.click("#add-remove-layer-btn") |
228 | 216 | ||
229 | notification = self.wait_until_visible("#change-notification-msg") | 217 | notification = self.wait_until_visible("#change-notification-msg") |
@@ -231,7 +219,7 @@ class TestLayerDetailsPage(SeleniumTestCase): | |||
231 | expected_text = "You have removed 1 layer from your project: %s" % \ | 219 | expected_text = "You have removed 1 layer from your project: %s" % \ |
232 | self.imported_layer_version.layer.name | 220 | self.imported_layer_version.layer.name |
233 | 221 | ||
234 | self.assertTrue(expected_text in notification.text, | 222 | self.assertIn(expected_text, notification.text, |
235 | "Expected notification text %s not found was " | 223 | "Expected notification text %s not found was " |
236 | " \"%s\" instead" % | 224 | " \"%s\" instead" % |
237 | (expected_text, notification.text)) | 225 | (expected_text, notification.text)) |
diff --git a/bitbake/lib/toaster/tests/browser/test_new_custom_image_page.py b/bitbake/lib/toaster/tests/browser/test_new_custom_image_page.py index 9f0b6397fe..bf0304dbec 100644 --- a/bitbake/lib/toaster/tests/browser/test_new_custom_image_page.py +++ b/bitbake/lib/toaster/tests/browser/test_new_custom_image_page.py | |||
@@ -90,7 +90,7 @@ class TestNewCustomImagePage(SeleniumTestCase): | |||
90 | """ | 90 | """ |
91 | url = reverse('newcustomimage', args=(self.project.id,)) | 91 | url = reverse('newcustomimage', args=(self.project.id,)) |
92 | self.get(url) | 92 | self.get(url) |
93 | self.wait_until_visible('#global-nav', poll=3) | 93 | self.wait_until_visible('#global-nav') |
94 | 94 | ||
95 | self.click('button[data-recipe="%s"]' % self.recipe.id) | 95 | self.click('button[data-recipe="%s"]' % self.recipe.id) |
96 | 96 | ||
diff --git a/bitbake/lib/toaster/tests/browser/test_new_project_page.py b/bitbake/lib/toaster/tests/browser/test_new_project_page.py index 458bb6538d..e50f236c32 100644 --- a/bitbake/lib/toaster/tests/browser/test_new_project_page.py +++ b/bitbake/lib/toaster/tests/browser/test_new_project_page.py | |||
@@ -47,7 +47,7 @@ class TestNewProjectPage(SeleniumTestCase): | |||
47 | 47 | ||
48 | url = reverse('newproject') | 48 | url = reverse('newproject') |
49 | self.get(url) | 49 | self.get(url) |
50 | self.wait_until_visible('#new-project-name', poll=3) | 50 | self.wait_until_visible('#new-project-name') |
51 | self.enter_text('#new-project-name', project_name) | 51 | self.enter_text('#new-project-name', project_name) |
52 | 52 | ||
53 | select = Select(self.find('#projectversion')) | 53 | select = Select(self.find('#projectversion')) |
@@ -58,7 +58,7 @@ class TestNewProjectPage(SeleniumTestCase): | |||
58 | # We should get redirected to the new project's page with the | 58 | # We should get redirected to the new project's page with the |
59 | # notification at the top | 59 | # notification at the top |
60 | element = self.wait_until_visible( | 60 | element = self.wait_until_visible( |
61 | '#project-created-notification', poll=3) | 61 | '#project-created-notification') |
62 | 62 | ||
63 | self.assertTrue(project_name in element.text, | 63 | self.assertTrue(project_name in element.text, |
64 | "New project name not in new project notification") | 64 | "New project name not in new project notification") |
@@ -79,7 +79,7 @@ class TestNewProjectPage(SeleniumTestCase): | |||
79 | 79 | ||
80 | url = reverse('newproject') | 80 | url = reverse('newproject') |
81 | self.get(url) | 81 | self.get(url) |
82 | self.wait_until_visible('#new-project-name', poll=3) | 82 | self.wait_until_visible('#new-project-name') |
83 | 83 | ||
84 | self.enter_text('#new-project-name', project_name) | 84 | self.enter_text('#new-project-name', project_name) |
85 | 85 | ||
@@ -89,12 +89,10 @@ class TestNewProjectPage(SeleniumTestCase): | |||
89 | radio = self.driver.find_element(By.ID, 'type-new') | 89 | radio = self.driver.find_element(By.ID, 'type-new') |
90 | radio.click() | 90 | radio.click() |
91 | 91 | ||
92 | self.click("#create-project-button") | 92 | self.wait_until_visible('#hint-error-project-name') |
93 | |||
94 | self.wait_until_present('#hint-error-project-name', poll=3) | ||
95 | element = self.find('#hint-error-project-name') | 93 | element = self.find('#hint-error-project-name') |
96 | 94 | ||
97 | self.assertTrue(("Project names must be unique" in element.text), | 95 | self.assertIn("Project names must be unique", element.text, |
98 | "Did not find unique project name error message") | 96 | "Did not find unique project name error message") |
99 | 97 | ||
100 | # Try and click it anyway, if it submits we'll have a new project in | 98 | # Try and click it anyway, if it submits we'll have a new project in |
diff --git a/bitbake/lib/toaster/tests/builds/buildtest.py b/bitbake/lib/toaster/tests/builds/buildtest.py index cacfccd4d3..e54d561334 100644 --- a/bitbake/lib/toaster/tests/builds/buildtest.py +++ b/bitbake/lib/toaster/tests/builds/buildtest.py | |||
@@ -128,7 +128,7 @@ class BuildTest(unittest.TestCase): | |||
128 | if os.environ.get("TOASTER_TEST_USE_SSTATE_MIRROR"): | 128 | if os.environ.get("TOASTER_TEST_USE_SSTATE_MIRROR"): |
129 | ProjectVariable.objects.get_or_create( | 129 | ProjectVariable.objects.get_or_create( |
130 | name="SSTATE_MIRRORS", | 130 | name="SSTATE_MIRRORS", |
131 | value="file://.* http://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH", | 131 | value="file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH", |
132 | project=project) | 132 | project=project) |
133 | 133 | ||
134 | ProjectTarget.objects.create(project=project, | 134 | ProjectTarget.objects.create(project=project, |
diff --git a/bitbake/lib/toaster/tests/functional/functional_helpers.py b/bitbake/lib/toaster/tests/functional/functional_helpers.py index 7c20437d14..e28f2024f5 100644 --- a/bitbake/lib/toaster/tests/functional/functional_helpers.py +++ b/bitbake/lib/toaster/tests/functional/functional_helpers.py | |||
@@ -12,9 +12,12 @@ import logging | |||
12 | import subprocess | 12 | import subprocess |
13 | import signal | 13 | import signal |
14 | import re | 14 | import re |
15 | import requests | ||
15 | 16 | ||
17 | from django.urls import reverse | ||
16 | from tests.browser.selenium_helpers_base import SeleniumTestCaseBase | 18 | from tests.browser.selenium_helpers_base import SeleniumTestCaseBase |
17 | from selenium.webdriver.common.by import By | 19 | from selenium.webdriver.common.by import By |
20 | from selenium.webdriver.support.select import Select | ||
18 | from selenium.common.exceptions import NoSuchElementException | 21 | from selenium.common.exceptions import NoSuchElementException |
19 | 22 | ||
20 | logger = logging.getLogger("toaster") | 23 | logger = logging.getLogger("toaster") |
@@ -136,3 +139,86 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase): | |||
136 | except NoSuchElementException: | 139 | except NoSuchElementException: |
137 | return False | 140 | return False |
138 | return element | 141 | return element |
142 | |||
143 | def create_new_project( | ||
144 | self, | ||
145 | project_name, | ||
146 | release, | ||
147 | release_title, | ||
148 | merge_toaster_settings, | ||
149 | ): | ||
150 | """ Create/Test new project using: | ||
151 | - Project Name: Any string | ||
152 | - Release: Any string | ||
153 | - Merge Toaster settings: True or False | ||
154 | """ | ||
155 | |||
156 | # Obtain a CSRF token from a suitable URL | ||
157 | projs = requests.get(self.live_server_url + reverse('newproject')) | ||
158 | csrftoken = projs.cookies.get('csrftoken') | ||
159 | |||
160 | # Use the projects typeahead to find out if the project already exists | ||
161 | req = requests.get(self.live_server_url + reverse('xhr_projectstypeahead'), {'search': project_name, 'format' : 'json'}) | ||
162 | data = req.json() | ||
163 | # Delete any existing projects | ||
164 | for result in data['results']: | ||
165 | del_url = reverse('xhr_project', args=(result['id'],)) | ||
166 | del_response = requests.delete(self.live_server_url + del_url, cookies={'csrftoken': csrftoken}, headers={'X-CSRFToken': csrftoken}) | ||
167 | self.assertEqual(del_response.status_code, 200) | ||
168 | |||
169 | self.get(reverse('newproject')) | ||
170 | self.wait_until_visible('#new-project-name') | ||
171 | self.driver.find_element(By.ID, | ||
172 | "new-project-name").send_keys(project_name) | ||
173 | |||
174 | select = Select(self.find('#projectversion')) | ||
175 | select.select_by_value(release) | ||
176 | |||
177 | # check merge toaster settings | ||
178 | checkbox = self.find('.checkbox-mergeattr') | ||
179 | if merge_toaster_settings: | ||
180 | if not checkbox.is_selected(): | ||
181 | checkbox.click() | ||
182 | else: | ||
183 | if checkbox.is_selected(): | ||
184 | checkbox.click() | ||
185 | |||
186 | self.wait_until_clickable('#create-project-button') | ||
187 | |||
188 | self.driver.find_element(By.ID, "create-project-button").click() | ||
189 | |||
190 | element = self.wait_until_visible('#project-created-notification') | ||
191 | self.assertTrue( | ||
192 | self.element_exists('#project-created-notification'), | ||
193 | f"Project:{project_name} creation notification not shown" | ||
194 | ) | ||
195 | self.assertTrue( | ||
196 | project_name in element.text, | ||
197 | f"New project name:{project_name} not in new project notification" | ||
198 | ) | ||
199 | |||
200 | # Use the projects typeahead again to check the project now exists | ||
201 | req = requests.get(self.live_server_url + reverse('xhr_projectstypeahead'), {'search': project_name, 'format' : 'json'}) | ||
202 | data = req.json() | ||
203 | self.assertGreater(len(data['results']), 0, f"New project:{project_name} not found in database") | ||
204 | |||
205 | project_id = data['results'][0]['id'] | ||
206 | |||
207 | self.wait_until_visible('#project-release-title') | ||
208 | |||
209 | # check release | ||
210 | if release_title is not None: | ||
211 | self.assertTrue(re.search( | ||
212 | release_title, | ||
213 | self.driver.find_element(By.XPATH, | ||
214 | "//span[@id='project-release-title']" | ||
215 | ).text), | ||
216 | 'The project release is not defined') | ||
217 | |||
218 | return project_id | ||
219 | |||
220 | def load_projects_page_helper(self): | ||
221 | self.wait_until_present('#projectstable') | ||
222 | # Need to wait for some data in the table too | ||
223 | self.wait_until_present('td[class="updated"]') | ||
224 | |||
diff --git a/bitbake/lib/toaster/tests/functional/test_create_new_project.py b/bitbake/lib/toaster/tests/functional/test_create_new_project.py index 94d90459e1..66213c736e 100644 --- a/bitbake/lib/toaster/tests/functional/test_create_new_project.py +++ b/bitbake/lib/toaster/tests/functional/test_create_new_project.py | |||
@@ -11,67 +11,10 @@ import pytest | |||
11 | from django.urls import reverse | 11 | from django.urls import reverse |
12 | from selenium.webdriver.support.select import Select | 12 | from selenium.webdriver.support.select import Select |
13 | from tests.functional.functional_helpers import SeleniumFunctionalTestCase | 13 | from tests.functional.functional_helpers import SeleniumFunctionalTestCase |
14 | from orm.models import Project | ||
15 | from selenium.webdriver.common.by import By | 14 | from selenium.webdriver.common.by import By |
16 | 15 | ||
17 | |||
18 | @pytest.mark.django_db | ||
19 | @pytest.mark.order("last") | ||
20 | class TestCreateNewProject(SeleniumFunctionalTestCase): | 16 | class TestCreateNewProject(SeleniumFunctionalTestCase): |
21 | 17 | ||
22 | def _create_test_new_project( | ||
23 | self, | ||
24 | project_name, | ||
25 | release, | ||
26 | release_title, | ||
27 | merge_toaster_settings, | ||
28 | ): | ||
29 | """ Create/Test new project using: | ||
30 | - Project Name: Any string | ||
31 | - Release: Any string | ||
32 | - Merge Toaster settings: True or False | ||
33 | """ | ||
34 | self.get(reverse('newproject')) | ||
35 | self.wait_until_visible('#new-project-name', poll=3) | ||
36 | self.driver.find_element(By.ID, | ||
37 | "new-project-name").send_keys(project_name) | ||
38 | |||
39 | select = Select(self.find('#projectversion')) | ||
40 | select.select_by_value(release) | ||
41 | |||
42 | # check merge toaster settings | ||
43 | checkbox = self.find('.checkbox-mergeattr') | ||
44 | if merge_toaster_settings: | ||
45 | if not checkbox.is_selected(): | ||
46 | checkbox.click() | ||
47 | else: | ||
48 | if checkbox.is_selected(): | ||
49 | checkbox.click() | ||
50 | |||
51 | self.driver.find_element(By.ID, "create-project-button").click() | ||
52 | |||
53 | element = self.wait_until_visible('#project-created-notification', poll=3) | ||
54 | self.assertTrue( | ||
55 | self.element_exists('#project-created-notification'), | ||
56 | f"Project:{project_name} creation notification not shown" | ||
57 | ) | ||
58 | self.assertTrue( | ||
59 | project_name in element.text, | ||
60 | f"New project name:{project_name} not in new project notification" | ||
61 | ) | ||
62 | self.assertTrue( | ||
63 | Project.objects.filter(name=project_name).count(), | ||
64 | f"New project:{project_name} not found in database" | ||
65 | ) | ||
66 | |||
67 | # check release | ||
68 | self.assertTrue(re.search( | ||
69 | release_title, | ||
70 | self.driver.find_element(By.XPATH, | ||
71 | "//span[@id='project-release-title']" | ||
72 | ).text), | ||
73 | 'The project release is not defined') | ||
74 | |||
75 | def test_create_new_project_master(self): | 18 | def test_create_new_project_master(self): |
76 | """ Test create new project using: | 19 | """ Test create new project using: |
77 | - Project Name: Any string | 20 | - Project Name: Any string |
@@ -81,43 +24,43 @@ class TestCreateNewProject(SeleniumFunctionalTestCase): | |||
81 | release = '3' | 24 | release = '3' |
82 | release_title = 'Yocto Project master' | 25 | release_title = 'Yocto Project master' |
83 | project_name = 'projectmaster' | 26 | project_name = 'projectmaster' |
84 | self._create_test_new_project( | 27 | self.create_new_project( |
85 | project_name, | 28 | project_name, |
86 | release, | 29 | release, |
87 | release_title, | 30 | release_title, |
88 | False, | 31 | False, |
89 | ) | 32 | ) |
90 | 33 | ||
91 | def test_create_new_project_kirkstone(self): | 34 | def test_create_new_project_scarthgap(self): |
92 | """ Test create new project using: | 35 | """ Test create new project using: |
93 | - Project Name: Any string | 36 | - Project Name: Any string |
94 | - Release: Yocto Project 4.0 "Kirkstone" (option value: 1) | 37 | - Release: Yocto Project 5.0 "Scarthgap" (option value: 1) |
95 | - Merge Toaster settings: True | 38 | - Merge Toaster settings: True |
96 | """ | 39 | """ |
97 | release = '1' | 40 | release = '1' |
98 | release_title = 'Yocto Project 4.0 "Kirkstone"' | 41 | release_title = 'Yocto Project 5.0 "Scarthgap"' |
99 | project_name = 'projectkirkstone' | 42 | project_name = 'projectscarthgap' |
100 | self._create_test_new_project( | 43 | self.create_new_project( |
101 | project_name, | 44 | project_name, |
102 | release, | 45 | release, |
103 | release_title, | 46 | release_title, |
104 | True, | 47 | True, |
105 | ) | 48 | ) |
106 | 49 | ||
107 | def test_create_new_project_dunfell(self): | 50 | def test_create_new_project_kirkstone(self): |
108 | """ Test create new project using: | 51 | """ Test create new project using: |
109 | - Project Name: Any string | 52 | - Project Name: Any string |
110 | - Release: Yocto Project 3.1 "Dunfell" (option value: 5) | 53 | - Release: Yocto Project 4.0 "Kirkstone" (option value: 6) |
111 | - Merge Toaster settings: False | 54 | - Merge Toaster settings: True |
112 | """ | 55 | """ |
113 | release = '5' | 56 | release = '7' |
114 | release_title = 'Yocto Project 3.1 "Dunfell"' | 57 | release_title = 'Yocto Project 4.0 "Kirkstone"' |
115 | project_name = 'projectdunfell' | 58 | project_name = 'projectkirkstone' |
116 | self._create_test_new_project( | 59 | self.create_new_project( |
117 | project_name, | 60 | project_name, |
118 | release, | 61 | release, |
119 | release_title, | 62 | release_title, |
120 | False, | 63 | True, |
121 | ) | 64 | ) |
122 | 65 | ||
123 | def test_create_new_project_local(self): | 66 | def test_create_new_project_local(self): |
@@ -129,7 +72,7 @@ class TestCreateNewProject(SeleniumFunctionalTestCase): | |||
129 | release = '2' | 72 | release = '2' |
130 | release_title = 'Local Yocto Project' | 73 | release_title = 'Local Yocto Project' |
131 | project_name = 'projectlocal' | 74 | project_name = 'projectlocal' |
132 | self._create_test_new_project( | 75 | self.create_new_project( |
133 | project_name, | 76 | project_name, |
134 | release, | 77 | release, |
135 | release_title, | 78 | release_title, |
@@ -172,8 +115,10 @@ class TestCreateNewProject(SeleniumFunctionalTestCase): | |||
172 | "import-project-dir").send_keys(wrong_path) | 115 | "import-project-dir").send_keys(wrong_path) |
173 | self.driver.find_element(By.ID, "create-project-button").click() | 116 | self.driver.find_element(By.ID, "create-project-button").click() |
174 | 117 | ||
118 | self.wait_until_visible('.alert-danger') | ||
119 | |||
175 | # check error message | 120 | # check error message |
176 | self.assertTrue(self.element_exists('.alert-danger'), | 121 | self.assertTrue(self.element_exists('.alert-danger'), |
177 | 'Allert message not shown') | 122 | 'Alert message not shown') |
178 | self.assertTrue(wrong_path in self.find('.alert-danger').text, | 123 | self.assertTrue(wrong_path in self.find('.alert-danger').text, |
179 | "Wrong path not in alert message") | 124 | "Wrong path not in alert message") |
diff --git a/bitbake/lib/toaster/tests/functional/test_functional_basic.py b/bitbake/lib/toaster/tests/functional/test_functional_basic.py index e4070fbb88..d5c9708617 100644 --- a/bitbake/lib/toaster/tests/functional/test_functional_basic.py +++ b/bitbake/lib/toaster/tests/functional/test_functional_basic.py | |||
@@ -17,145 +17,132 @@ from selenium.webdriver.common.by import By | |||
17 | from tests.functional.utils import get_projectId_from_url | 17 | from tests.functional.utils import get_projectId_from_url |
18 | 18 | ||
19 | 19 | ||
20 | @pytest.mark.django_db | ||
21 | @pytest.mark.order("second_to_last") | ||
22 | class FuntionalTestBasic(SeleniumFunctionalTestCase): | 20 | class FuntionalTestBasic(SeleniumFunctionalTestCase): |
23 | """Basic functional tests for Toaster""" | 21 | """Basic functional tests for Toaster""" |
24 | project_id = None | 22 | project_id = None |
23 | project_url = None | ||
25 | 24 | ||
26 | def setUp(self): | 25 | def setUp(self): |
27 | super(FuntionalTestBasic, self).setUp() | 26 | super(FuntionalTestBasic, self).setUp() |
28 | if not FuntionalTestBasic.project_id: | 27 | if not FuntionalTestBasic.project_id: |
29 | self._create_slenium_project() | 28 | FuntionalTestBasic.project_id = self.create_new_project('selenium-project', '3', None, False) |
30 | current_url = self.driver.current_url | ||
31 | FuntionalTestBasic.project_id = get_projectId_from_url(current_url) | ||
32 | |||
33 | # testcase (1514) | ||
34 | def _create_slenium_project(self): | ||
35 | project_name = 'selenium-project' | ||
36 | self.get(reverse('newproject')) | ||
37 | self.wait_until_visible('#new-project-name', poll=3) | ||
38 | self.driver.find_element(By.ID, "new-project-name").send_keys(project_name) | ||
39 | self.driver.find_element(By.ID, 'projectversion').click() | ||
40 | self.driver.find_element(By.ID, "create-project-button").click() | ||
41 | element = self.wait_until_visible('#project-created-notification', poll=10) | ||
42 | self.assertTrue(self.element_exists('#project-created-notification'),'Project creation notification not shown') | ||
43 | self.assertTrue(project_name in element.text, | ||
44 | "New project name not in new project notification") | ||
45 | self.assertTrue(Project.objects.filter(name=project_name).count(), | ||
46 | "New project not found in database") | ||
47 | return Project.objects.last().id | ||
48 | 29 | ||
49 | # testcase (1515) | 30 | # testcase (1515) |
50 | def test_verify_left_bar_menu(self): | 31 | def test_verify_left_bar_menu(self): |
51 | self.get(reverse('all-projects')) | 32 | self.get(reverse('all-projects')) |
52 | self.wait_until_present('#projectstable', poll=10) | 33 | self.load_projects_page_helper() |
53 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 34 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
54 | self.wait_until_present('#config-nav', poll=10) | 35 | self.wait_until_present('#config-nav') |
55 | self.assertTrue(self.element_exists('#config-nav'),'Configuration Tab does not exist') | 36 | self.assertTrue(self.element_exists('#config-nav'),'Configuration Tab does not exist') |
56 | project_URL=self.get_URL() | 37 | project_URL=self.get_URL() |
57 | self.driver.find_element(By.XPATH, '//a[@href="'+project_URL+'"]').click() | 38 | self.driver.find_element(By.XPATH, '//a[@href="'+project_URL+'"]').click() |
58 | self.wait_until_present('#config-nav', poll=10) | ||
59 | 39 | ||
60 | try: | 40 | try: |
41 | self.wait_until_present('#config-nav') | ||
61 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click() | 42 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click() |
62 | self.wait_until_present('#config-nav', poll=10) | 43 | self.wait_until_present('#filter-modal-customimagestable') |
63 | self.assertTrue(re.search("Custom images",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'Custom images information is not loading properly') | ||
64 | except: | 44 | except: |
65 | self.fail(msg='No Custom images tab available') | 45 | self.fail(msg='No Custom images tab available') |
46 | self.assertTrue(re.search("Custom images",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'Custom images information is not loading properly') | ||
66 | 47 | ||
67 | try: | 48 | try: |
68 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click() | 49 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click() |
69 | self.wait_until_present('#config-nav', poll=10) | 50 | self.wait_until_present('#filter-modal-imagerecipestable') |
70 | self.assertTrue(re.search("Compatible image recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible image recipes information is not loading properly') | ||
71 | except: | 51 | except: |
72 | self.fail(msg='No Compatible image tab available') | 52 | self.fail(msg='No Compatible image tab available') |
53 | self.assertTrue(re.search("Compatible image recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible image recipes information is not loading properly') | ||
73 | 54 | ||
74 | try: | 55 | try: |
75 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click() | 56 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click() |
76 | self.wait_until_present('#config-nav', poll=10) | 57 | self.wait_until_present('#filter-modal-softwarerecipestable') |
77 | self.assertTrue(re.search("Compatible software recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible software recipe information is not loading properly') | ||
78 | except: | 58 | except: |
79 | self.fail(msg='No Compatible software recipe tab available') | 59 | self.fail(msg='No Compatible software recipe tab available') |
60 | self.assertTrue(re.search("Compatible software recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible software recipe information is not loading properly') | ||
80 | 61 | ||
81 | try: | 62 | try: |
82 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click() | 63 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click() |
83 | self.wait_until_present('#config-nav', poll=10) | 64 | self.wait_until_present('#filter-modal-machinestable') |
84 | self.assertTrue(re.search("Compatible machines",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible machine information is not loading properly') | ||
85 | except: | 65 | except: |
86 | self.fail(msg='No Compatible machines tab available') | 66 | self.fail(msg='No Compatible machines tab available') |
67 | self.assertTrue(re.search("Compatible machines",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible machine information is not loading properly') | ||
87 | 68 | ||
88 | try: | 69 | try: |
89 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click() | 70 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click() |
90 | self.wait_until_present('#config-nav', poll=10) | 71 | self.wait_until_present('#filter-modal-layerstable') |
91 | self.assertTrue(re.search("Compatible layers",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible layer information is not loading properly') | ||
92 | except: | 72 | except: |
93 | self.fail(msg='No Compatible layers tab available') | 73 | self.fail(msg='No Compatible layers tab available') |
74 | self.assertTrue(re.search("Compatible layers",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible layer information is not loading properly') | ||
94 | 75 | ||
95 | try: | 76 | try: |
96 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click() | 77 | self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click() |
97 | self.wait_until_present('#config-nav', poll=10) | 78 | self.wait_until_present('#configvar-list') |
98 | self.assertTrue(re.search("Bitbake variables",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Bitbake variables information is not loading properly') | ||
99 | except: | 79 | except: |
100 | self.fail(msg='No Bitbake variables tab available') | 80 | self.fail(msg='No Bitbake variables tab available') |
81 | self.assertTrue(re.search("Bitbake variables",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Bitbake variables information is not loading properly') | ||
101 | 82 | ||
102 | # testcase (1516) | 83 | # testcase (1516) |
103 | def test_review_configuration_information(self): | 84 | def test_review_configuration_information(self): |
104 | self.get(reverse('all-projects')) | 85 | self.get(reverse('all-projects')) |
105 | self.wait_until_present('#projectstable', poll=10) | 86 | self.load_projects_page_helper() |
106 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 87 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
107 | project_URL=self.get_URL() | 88 | project_URL=self.get_URL() |
108 | self.wait_until_present('#config-nav', poll=10) | 89 | |
90 | # Machine section of page | ||
91 | self.wait_until_visible('#machine-section') | ||
92 | self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist') | ||
93 | self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.XPATH, "//span[@id='project-machine-name']").text),'The machine type is not assigned') | ||
109 | try: | 94 | try: |
110 | self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist') | ||
111 | self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.XPATH, "//span[@id='project-machine-name']").text),'The machine type is not assigned') | ||
112 | self.driver.find_element(By.XPATH, "//span[@id='change-machine-toggle']").click() | 95 | self.driver.find_element(By.XPATH, "//span[@id='change-machine-toggle']").click() |
113 | self.wait_until_visible('#select-machine-form', poll=10) | 96 | self.wait_until_visible('#select-machine-form') |
114 | self.wait_until_visible('#cancel-machine-change', poll=10) | 97 | self.wait_until_visible('#cancel-machine-change') |
115 | self.driver.find_element(By.XPATH, "//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click() | 98 | self.driver.find_element(By.XPATH, "//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click() |
116 | except: | 99 | except: |
117 | self.fail(msg='The machine information is wrong in the configuration page') | 100 | self.fail(msg='The machine information is wrong in the configuration page') |
118 | 101 | ||
102 | # Most built recipes section | ||
103 | self.wait_until_visible('#no-most-built') | ||
119 | try: | 104 | try: |
120 | self.driver.find_element(By.ID, 'no-most-built') | 105 | self.driver.find_element(By.ID, 'no-most-built') |
121 | except: | 106 | except: |
122 | self.fail(msg='No Most built information in project detail page') | 107 | self.fail(msg='No Most built information in project detail page') |
123 | 108 | ||
124 | try: | 109 | # Project Release title |
125 | self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.XPATH, "//span[@id='project-release-title']").text),'The project release is not defined') | 110 | self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.XPATH, "//span[@id='project-release-title']").text), 'The project release is not defined in the project detail page') |
126 | except: | ||
127 | self.fail(msg='No project release title information in project detail page') | ||
128 | 111 | ||
112 | # List of layers in project | ||
113 | self.wait_until_visible('#layer-container') | ||
114 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']") | ||
115 | self.assertTrue(re.search("3",self.driver.find_element(By.ID, "project-layers-count").text),'There should be 3 layers listed in the layer count') | ||
129 | try: | 116 | try: |
130 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']") | ||
131 | self.assertTrue(re.search("3",self.driver.find_element(By.ID, "project-layers-count").text),'There should be 3 layers listed in the layer count') | ||
132 | layer_list = self.driver.find_element(By.ID, "layers-in-project-list") | 117 | layer_list = self.driver.find_element(By.ID, "layers-in-project-list") |
133 | layers = layer_list.find_elements(By.TAG_NAME, "li") | 118 | layers = layer_list.find_elements(By.TAG_NAME, "li") |
134 | for layer in layers: | ||
135 | if re.match ("openembedded-core",layer.text): | ||
136 | print ("openembedded-core layer is a default layer in the project configuration") | ||
137 | elif re.match ("meta-poky",layer.text): | ||
138 | print ("meta-poky layer is a default layer in the project configuration") | ||
139 | elif re.match ("meta-yocto-bsp",layer.text): | ||
140 | print ("meta-yocto-bsp is a default layer in the project configuratoin") | ||
141 | else: | ||
142 | self.fail(msg='default layers are missing from the project configuration') | ||
143 | except: | 119 | except: |
144 | self.fail(msg='No Layer information in project detail page') | 120 | self.fail(msg='No Layer information in project detail page') |
145 | 121 | ||
122 | for layer in layers: | ||
123 | if re.match ("openembedded-core", layer.text): | ||
124 | print ("openembedded-core layer is a default layer in the project configuration") | ||
125 | elif re.match ("meta-poky", layer.text): | ||
126 | print ("meta-poky layer is a default layer in the project configuration") | ||
127 | elif re.match ("meta-yocto-bsp", layer.text): | ||
128 | print ("meta-yocto-bsp is a default layer in the project configuratoin") | ||
129 | else: | ||
130 | self.fail(msg='default layers are missing from the project configuration') | ||
131 | |||
146 | # testcase (1517) | 132 | # testcase (1517) |
147 | def test_verify_machine_information(self): | 133 | def test_verify_machine_information(self): |
148 | self.get(reverse('all-projects')) | 134 | self.get(reverse('all-projects')) |
149 | self.wait_until_present('#projectstable', poll=10) | 135 | self.load_projects_page_helper() |
150 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 136 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
151 | self.wait_until_present('#config-nav', poll=10) | ||
152 | 137 | ||
138 | self.wait_until_visible('#machine-section') | ||
139 | self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist') | ||
140 | self.wait_until_visible('#project-machine-name') | ||
141 | self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.ID, "project-machine-name").text),'The machine type is not assigned') | ||
153 | try: | 142 | try: |
154 | self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist') | ||
155 | self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.ID, "project-machine-name").text),'The machine type is not assigned') | ||
156 | self.driver.find_element(By.ID, "change-machine-toggle").click() | 143 | self.driver.find_element(By.ID, "change-machine-toggle").click() |
157 | self.wait_until_visible('#select-machine-form', poll=10) | 144 | self.wait_until_visible('#select-machine-form') |
158 | self.wait_until_visible('#cancel-machine-change', poll=10) | 145 | self.wait_until_visible('#cancel-machine-change') |
159 | self.driver.find_element(By.ID, "cancel-machine-change").click() | 146 | self.driver.find_element(By.ID, "cancel-machine-change").click() |
160 | except: | 147 | except: |
161 | self.fail(msg='The machine information is wrong in the configuration page') | 148 | self.fail(msg='The machine information is wrong in the configuration page') |
@@ -163,83 +150,95 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase): | |||
163 | # testcase (1518) | 150 | # testcase (1518) |
164 | def test_verify_most_built_recipes_information(self): | 151 | def test_verify_most_built_recipes_information(self): |
165 | self.get(reverse('all-projects')) | 152 | self.get(reverse('all-projects')) |
166 | self.wait_until_present('#projectstable', poll=10) | 153 | self.load_projects_page_helper() |
167 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 154 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
168 | self.wait_until_present('#config-nav', poll=10) | 155 | self.wait_until_present('#config-nav') |
169 | project_URL=self.get_URL() | 156 | project_URL=self.get_URL() |
157 | |||
158 | self.wait_until_visible('#no-most-built') | ||
159 | self.assertTrue(re.search("You haven't built any recipes yet",self.driver.find_element(By.ID, "no-most-built").text),'Default message of no builds is not present') | ||
170 | try: | 160 | try: |
171 | self.assertTrue(re.search("You haven't built any recipes yet",self.driver.find_element(By.ID, "no-most-built").text),'Default message of no builds is not present') | ||
172 | self.driver.find_element(By.XPATH, "//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click() | 161 | self.driver.find_element(By.XPATH, "//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click() |
173 | self.wait_until_present('#config-nav', poll=10) | ||
174 | self.assertTrue(re.search("Compatible image recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Choose a recipe to build link is not working properly') | ||
175 | except: | 162 | except: |
176 | self.fail(msg='No Most built information in project detail page') | 163 | self.fail(msg='No Most built information in project detail page') |
164 | self.wait_until_visible('#config-nav') | ||
165 | self.assertTrue(re.search("Compatible image recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Choose a recipe to build link is not working properly') | ||
177 | 166 | ||
178 | # testcase (1519) | 167 | # testcase (1519) |
179 | def test_verify_project_release_information(self): | 168 | def test_verify_project_release_information(self): |
180 | self.get(reverse('all-projects')) | 169 | self.get(reverse('all-projects')) |
181 | self.wait_until_present('#projectstable', poll=10) | 170 | self.load_projects_page_helper() |
182 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 171 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
183 | self.wait_until_present('#config-nav', poll=10) | 172 | self.wait_until_visible('#project-release-title') |
184 | 173 | self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.ID, "project-release-title").text), 'No project release title information in project detail page') | |
185 | try: | ||
186 | self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.ID, "project-release-title").text),'The project release is not defined') | ||
187 | except: | ||
188 | self.fail(msg='No project release title information in project detail page') | ||
189 | 174 | ||
190 | # testcase (1520) | 175 | # testcase (1520) |
191 | def test_verify_layer_information(self): | 176 | def test_verify_layer_information(self): |
192 | self.get(reverse('all-projects')) | 177 | self.get(reverse('all-projects')) |
193 | self.wait_until_present('#projectstable', poll=10) | 178 | self.load_projects_page_helper() |
194 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 179 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
195 | self.wait_until_present('#config-nav', poll=10) | 180 | self.wait_until_present('#config-nav') |
196 | project_URL=self.get_URL() | 181 | project_URL=self.get_URL() |
182 | self.wait_until_visible('#layer-container') | ||
183 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']") | ||
184 | self.wait_until_visible('#project-layers-count') | ||
185 | self.assertTrue(re.search("3",self.driver.find_element(By.ID, "project-layers-count").text),'There should be 3 layers listed in the layer count') | ||
186 | |||
197 | try: | 187 | try: |
198 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']") | ||
199 | self.assertTrue(re.search("3",self.driver.find_element(By.ID, "project-layers-count").text),'There should be 3 layers listed in the layer count') | ||
200 | layer_list = self.driver.find_element(By.ID, "layers-in-project-list") | 188 | layer_list = self.driver.find_element(By.ID, "layers-in-project-list") |
201 | layers = layer_list.find_elements(By.TAG_NAME, "li") | 189 | layers = layer_list.find_elements(By.TAG_NAME, "li") |
190 | except: | ||
191 | self.fail(msg='No Layer information in project detail page') | ||
202 | 192 | ||
203 | for layer in layers: | 193 | for layer in layers: |
204 | if re.match ("openembedded-core",layer.text): | 194 | if re.match ("openembedded-core",layer.text): |
205 | print ("openembedded-core layer is a default layer in the project configuration") | 195 | print ("openembedded-core layer is a default layer in the project configuration") |
206 | elif re.match ("meta-poky",layer.text): | 196 | elif re.match ("meta-poky",layer.text): |
207 | print ("meta-poky layer is a default layer in the project configuration") | 197 | print ("meta-poky layer is a default layer in the project configuration") |
208 | elif re.match ("meta-yocto-bsp",layer.text): | 198 | elif re.match ("meta-yocto-bsp",layer.text): |
209 | print ("meta-yocto-bsp is a default layer in the project configuratoin") | 199 | print ("meta-yocto-bsp is a default layer in the project configuratoin") |
210 | else: | 200 | else: |
211 | self.fail(msg='default layers are missing from the project configuration') | 201 | self.fail(msg='default layers are missing from the project configuration') |
212 | 202 | ||
203 | try: | ||
213 | self.driver.find_element(By.XPATH, "//input[@id='layer-add-input']") | 204 | self.driver.find_element(By.XPATH, "//input[@id='layer-add-input']") |
214 | self.driver.find_element(By.XPATH, "//button[@id='add-layer-btn']") | 205 | self.driver.find_element(By.XPATH, "//button[@id='add-layer-btn']") |
215 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']") | 206 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']") |
216 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]") | 207 | self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]") |
217 | except: | 208 | except: |
218 | self.fail(msg='No Layer information in project detail page') | 209 | self.fail(msg='Layer configuration controls missing') |
219 | 210 | ||
220 | # testcase (1521) | 211 | # testcase (1521) |
221 | def test_verify_project_detail_links(self): | 212 | def test_verify_project_detail_links(self): |
222 | self.get(reverse('all-projects')) | 213 | self.get(reverse('all-projects')) |
223 | self.wait_until_present('#projectstable', poll=10) | 214 | self.load_projects_page_helper() |
224 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() | 215 | self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() |
225 | self.wait_until_present('#config-nav', poll=10) | 216 | self.wait_until_present('#config-nav') |
226 | project_URL=self.get_URL() | 217 | project_URL=self.get_URL() |
227 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").click() | 218 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").click() |
228 | self.wait_until_present('#config-nav', poll=10) | 219 | self.wait_until_visible('#topbar-configuration-tab') |
229 | self.assertTrue(re.search("Configuration",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").text), 'Configuration tab in project topbar is misspelled') | 220 | self.assertTrue(re.search("Configuration",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").text), 'Configuration tab in project topbar is misspelled') |
230 | 221 | ||
231 | try: | 222 | try: |
232 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click() | 223 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click() |
233 | self.wait_until_visible('#project-topbar', poll=10) | 224 | except: |
234 | self.assertTrue(re.search("Builds",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").text), 'Builds tab in project topbar is misspelled') | 225 | self.fail(msg='Builds tab information is not present') |
226 | |||
227 | self.wait_until_visible('#project-topbar') | ||
228 | self.assertTrue(re.search("Builds",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").text), 'Builds tab in project topbar is misspelled') | ||
229 | try: | ||
235 | self.driver.find_element(By.XPATH, "//div[@id='empty-state-projectbuildstable']") | 230 | self.driver.find_element(By.XPATH, "//div[@id='empty-state-projectbuildstable']") |
236 | except: | 231 | except: |
237 | self.fail(msg='Builds tab information is not present') | 232 | self.fail(msg='Builds tab information is not present') |
238 | 233 | ||
239 | try: | 234 | try: |
240 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click() | 235 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click() |
241 | self.wait_until_visible('#project-topbar', poll=10) | 236 | except: |
242 | self.assertTrue(re.search("Import layer",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").text), 'Import layer tab in project topbar is misspelled') | 237 | self.fail(msg='Import layer tab not loading properly') |
238 | |||
239 | self.wait_until_visible('#project-topbar') | ||
240 | self.assertTrue(re.search("Import layer",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").text), 'Import layer tab in project topbar is misspelled') | ||
241 | try: | ||
243 | self.driver.find_element(By.XPATH, "//fieldset[@id='repo-select']") | 242 | self.driver.find_element(By.XPATH, "//fieldset[@id='repo-select']") |
244 | self.driver.find_element(By.XPATH, "//fieldset[@id='git-repo']") | 243 | self.driver.find_element(By.XPATH, "//fieldset[@id='git-repo']") |
245 | except: | 244 | except: |
@@ -247,11 +246,12 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase): | |||
247 | 246 | ||
248 | try: | 247 | try: |
249 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click() | 248 | self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click() |
250 | self.wait_until_visible('#project-topbar', poll=10) | ||
251 | self.assertTrue(re.search("New custom image",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").text), 'New custom image tab in project topbar is misspelled') | ||
252 | self.assertTrue(re.search("Select the image recipe you want to customise",self.driver.find_element(By.XPATH, "//div[@class='col-md-12']/h2").text),'The new custom image tab is not loading correctly') | ||
253 | except: | 249 | except: |
254 | self.fail(msg='New custom image tab not loading properly') | 250 | self.fail(msg='New custom image tab not loading properly') |
255 | 251 | ||
252 | self.wait_until_visible('#project-topbar') | ||
253 | self.assertTrue(re.search("New custom image",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").text), 'New custom image tab in project topbar is misspelled') | ||
254 | self.assertTrue(re.search("Select the image recipe you want to customise",self.driver.find_element(By.XPATH, "//div[@class='col-md-12']/h2").text),'The new custom image tab is not loading correctly') | ||
255 | |||
256 | 256 | ||
257 | 257 | ||
diff --git a/bitbake/lib/toaster/tests/functional/test_project_config.py b/bitbake/lib/toaster/tests/functional/test_project_config.py index dbee36aa4e..fcb1bc3284 100644 --- a/bitbake/lib/toaster/tests/functional/test_project_config.py +++ b/bitbake/lib/toaster/tests/functional/test_project_config.py | |||
@@ -7,7 +7,6 @@ | |||
7 | # | 7 | # |
8 | 8 | ||
9 | import string | 9 | import string |
10 | import random | ||
11 | import pytest | 10 | import pytest |
12 | from django.urls import reverse | 11 | from django.urls import reverse |
13 | from selenium.webdriver import Keys | 12 | from selenium.webdriver import Keys |
@@ -18,9 +17,6 @@ from selenium.webdriver.common.by import By | |||
18 | 17 | ||
19 | from .utils import get_projectId_from_url | 18 | from .utils import get_projectId_from_url |
20 | 19 | ||
21 | |||
22 | @pytest.mark.django_db | ||
23 | @pytest.mark.order("last") | ||
24 | class TestProjectConfig(SeleniumFunctionalTestCase): | 20 | class TestProjectConfig(SeleniumFunctionalTestCase): |
25 | project_id = None | 21 | project_id = None |
26 | PROJECT_NAME = 'TestProjectConfig' | 22 | PROJECT_NAME = 'TestProjectConfig' |
@@ -28,42 +24,6 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
28 | INVALID_PATH_CHAR_TEXT = 'The directory path cannot include spaces or ' \ | 24 | INVALID_PATH_CHAR_TEXT = 'The directory path cannot include spaces or ' \ |
29 | 'any of these characters' | 25 | 'any of these characters' |
30 | 26 | ||
31 | def _create_project(self, project_name): | ||
32 | """ Create/Test new project using: | ||
33 | - Project Name: Any string | ||
34 | - Release: Any string | ||
35 | - Merge Toaster settings: True or False | ||
36 | """ | ||
37 | self.get(reverse('newproject')) | ||
38 | self.wait_until_visible('#new-project-name', poll=2) | ||
39 | self.find("#new-project-name").send_keys(project_name) | ||
40 | select = Select(self.find("#projectversion")) | ||
41 | select.select_by_value('3') | ||
42 | |||
43 | # check merge toaster settings | ||
44 | checkbox = self.find('.checkbox-mergeattr') | ||
45 | if not checkbox.is_selected(): | ||
46 | checkbox.click() | ||
47 | |||
48 | if self.PROJECT_NAME != 'TestProjectConfig': | ||
49 | # Reset project name if it's not the default one | ||
50 | self.PROJECT_NAME = 'TestProjectConfig' | ||
51 | |||
52 | self.find("#create-project-button").click() | ||
53 | |||
54 | try: | ||
55 | self.wait_until_visible('#hint-error-project-name', poll=2) | ||
56 | url = reverse('project', args=(TestProjectConfig.project_id, )) | ||
57 | self.get(url) | ||
58 | self.wait_until_visible('#config-nav', poll=3) | ||
59 | except TimeoutException: | ||
60 | self.wait_until_visible('#config-nav', poll=3) | ||
61 | |||
62 | def _random_string(self, length): | ||
63 | return ''.join( | ||
64 | random.choice(string.ascii_letters) for _ in range(length) | ||
65 | ) | ||
66 | |||
67 | def _get_config_nav_item(self, index): | 27 | def _get_config_nav_item(self, index): |
68 | config_nav = self.find('#config-nav') | 28 | config_nav = self.find('#config-nav') |
69 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] | 29 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] |
@@ -72,16 +32,14 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
72 | """ Navigate to project BitBake variables page """ | 32 | """ Navigate to project BitBake variables page """ |
73 | # check if the menu is displayed | 33 | # check if the menu is displayed |
74 | if TestProjectConfig.project_id is None: | 34 | if TestProjectConfig.project_id is None: |
75 | self._create_project(project_name=self._random_string(10)) | 35 | TestProjectConfig.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True) |
76 | current_url = self.driver.current_url | 36 | |
77 | TestProjectConfig.project_id = get_projectId_from_url(current_url) | 37 | url = reverse('projectconf', args=(TestProjectConfig.project_id,)) |
78 | else: | 38 | self.get(url) |
79 | url = reverse('projectconf', args=(TestProjectConfig.project_id,)) | 39 | self.wait_until_visible('#config-nav') |
80 | self.get(url) | ||
81 | self.wait_until_visible('#config-nav', poll=3) | ||
82 | bbv_page_link = self._get_config_nav_item(9) | 40 | bbv_page_link = self._get_config_nav_item(9) |
83 | bbv_page_link.click() | 41 | bbv_page_link.click() |
84 | self.wait_until_visible('#config-nav', poll=3) | 42 | self.wait_until_visible('#config-nav') |
85 | 43 | ||
86 | def test_no_underscore_iamgefs_type(self): | 44 | def test_no_underscore_iamgefs_type(self): |
87 | """ | 45 | """ |
@@ -90,13 +48,13 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
90 | self._navigate_bbv_page() | 48 | self._navigate_bbv_page() |
91 | imagefs_type = "foo_bar" | 49 | imagefs_type = "foo_bar" |
92 | 50 | ||
93 | self.wait_until_visible('#change-image_fstypes-icon', poll=2) | 51 | self.wait_until_visible('#change-image_fstypes-icon') |
94 | 52 | ||
95 | self.click('#change-image_fstypes-icon') | 53 | self.click('#change-image_fstypes-icon') |
96 | 54 | ||
97 | self.enter_text('#new-imagefs_types', imagefs_type) | 55 | self.enter_text('#new-imagefs_types', imagefs_type) |
98 | 56 | ||
99 | element = self.wait_until_visible('#hintError-image-fs_type', poll=2) | 57 | element = self.wait_until_visible('#hintError-image-fs_type') |
100 | 58 | ||
101 | self.assertTrue(("A valid image type cannot include underscores" in element.text), | 59 | self.assertTrue(("A valid image type cannot include underscores" in element.text), |
102 | "Did not find underscore error message") | 60 | "Did not find underscore error message") |
@@ -110,7 +68,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
110 | 68 | ||
111 | imagefs_type = "btrfs" | 69 | imagefs_type = "btrfs" |
112 | 70 | ||
113 | self.wait_until_visible('#change-image_fstypes-icon', poll=2) | 71 | self.wait_until_visible('#change-image_fstypes-icon') |
114 | 72 | ||
115 | self.click('#change-image_fstypes-icon') | 73 | self.click('#change-image_fstypes-icon') |
116 | 74 | ||
@@ -129,22 +87,20 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
129 | """ | 87 | """ |
130 | self._navigate_bbv_page() | 88 | self._navigate_bbv_page() |
131 | 89 | ||
132 | self.wait_until_visible('#change-image_fstypes-icon', poll=2) | 90 | self.wait_until_visible('#change-image_fstypes-icon') |
133 | |||
134 | self.click('#change-image_fstypes-icon') | 91 | self.click('#change-image_fstypes-icon') |
135 | 92 | ||
136 | checkboxes_selector = '.fs-checkbox-fstypes' | 93 | checkboxes_selector = '.fs-checkbox-fstypes' |
137 | 94 | ||
138 | self.wait_until_visible(checkboxes_selector, poll=2) | 95 | self.wait_until_visible(checkboxes_selector) |
139 | checkboxes = self.find_all(checkboxes_selector) | 96 | checkboxes = self.find_all(checkboxes_selector) |
140 | 97 | ||
141 | for checkbox in checkboxes: | 98 | for checkbox in checkboxes: |
142 | if checkbox.get_attribute("value") == "cpio": | 99 | if checkbox.get_attribute("value") == "cpio": |
143 | checkbox.click() | 100 | checkbox.click() |
101 | self.wait_until_visible('#new-imagefs_types') | ||
144 | element = self.driver.find_element(By.ID, 'new-imagefs_types') | 102 | element = self.driver.find_element(By.ID, 'new-imagefs_types') |
145 | 103 | ||
146 | self.wait_until_visible('#new-imagefs_types', poll=2) | ||
147 | |||
148 | self.assertTrue(("cpio" in element.get_attribute('value'), | 104 | self.assertTrue(("cpio" in element.get_attribute('value'), |
149 | "Imagefs not added into the textbox")) | 105 | "Imagefs not added into the textbox")) |
150 | checkbox.click() | 106 | checkbox.click() |
@@ -160,20 +116,19 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
160 | 116 | ||
161 | # activate the input to edit download dir | 117 | # activate the input to edit download dir |
162 | try: | 118 | try: |
163 | change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2) | 119 | change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon') |
164 | except TimeoutException: | 120 | except TimeoutException: |
165 | # If download dir is not displayed, test is skipped | 121 | # If download dir is not displayed, test is skipped |
166 | change_dl_dir_btn = None | 122 | change_dl_dir_btn = None |
167 | 123 | ||
168 | if change_dl_dir_btn: | 124 | if change_dl_dir_btn: |
169 | change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2) | ||
170 | change_dl_dir_btn.click() | 125 | change_dl_dir_btn.click() |
171 | 126 | ||
172 | # downloads dir path doesn't start with / or ${...} | 127 | # downloads dir path doesn't start with / or ${...} |
173 | input_field = self.wait_until_visible('#new-dl_dir', poll=2) | 128 | input_field = self.wait_until_visible('#new-dl_dir') |
174 | input_field.clear() | 129 | input_field.clear() |
175 | self.enter_text('#new-dl_dir', 'home/foo') | 130 | self.enter_text('#new-dl_dir', 'home/foo') |
176 | element = self.wait_until_visible('#hintError-initialChar-dl_dir', poll=2) | 131 | element = self.wait_until_visible('#hintError-initialChar-dl_dir') |
177 | 132 | ||
178 | msg = 'downloads directory path starts with invalid character but ' \ | 133 | msg = 'downloads directory path starts with invalid character but ' \ |
179 | 'treated as valid' | 134 | 'treated as valid' |
@@ -183,7 +138,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
183 | self.driver.find_element(By.ID, 'new-dl_dir').clear() | 138 | self.driver.find_element(By.ID, 'new-dl_dir').clear() |
184 | self.enter_text('#new-dl_dir', '/foo/bar a') | 139 | self.enter_text('#new-dl_dir', '/foo/bar a') |
185 | 140 | ||
186 | element = self.wait_until_visible('#hintError-dl_dir', poll=2) | 141 | element = self.wait_until_visible('#hintError-dl_dir') |
187 | msg = 'downloads directory path characters invalid but treated as valid' | 142 | msg = 'downloads directory path characters invalid but treated as valid' |
188 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | 143 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) |
189 | 144 | ||
@@ -191,7 +146,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
191 | self.driver.find_element(By.ID,'new-dl_dir').clear() | 146 | self.driver.find_element(By.ID,'new-dl_dir').clear() |
192 | self.enter_text('#new-dl_dir', '${TOPDIR}/down foo') | 147 | self.enter_text('#new-dl_dir', '${TOPDIR}/down foo') |
193 | 148 | ||
194 | element = self.wait_until_visible('#hintError-dl_dir', poll=2) | 149 | element = self.wait_until_visible('#hintError-dl_dir') |
195 | msg = 'downloads directory path characters invalid but treated as valid' | 150 | msg = 'downloads directory path characters invalid but treated as valid' |
196 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | 151 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) |
197 | 152 | ||
@@ -219,10 +174,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
219 | self._navigate_bbv_page() | 174 | self._navigate_bbv_page() |
220 | 175 | ||
221 | try: | 176 | try: |
222 | btn_chg_sstate_dir = self.wait_until_visible( | 177 | btn_chg_sstate_dir = self.wait_until_visible('#change-sstate_dir-icon') |
223 | '#change-sstate_dir-icon', | ||
224 | poll=2 | ||
225 | ) | ||
226 | self.click('#change-sstate_dir-icon') | 178 | self.click('#change-sstate_dir-icon') |
227 | except TimeoutException: | 179 | except TimeoutException: |
228 | # If sstate_dir is not displayed, test is skipped | 180 | # If sstate_dir is not displayed, test is skipped |
@@ -230,10 +182,10 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
230 | 182 | ||
231 | if btn_chg_sstate_dir: # Skip continuation if sstate_dir is not displayed | 183 | if btn_chg_sstate_dir: # Skip continuation if sstate_dir is not displayed |
232 | # path doesn't start with / or ${...} | 184 | # path doesn't start with / or ${...} |
233 | input_field = self.wait_until_visible('#new-sstate_dir', poll=2) | 185 | input_field = self.wait_until_visible('#new-sstate_dir') |
234 | input_field.clear() | 186 | input_field.clear() |
235 | self.enter_text('#new-sstate_dir', 'home/foo') | 187 | self.enter_text('#new-sstate_dir', 'home/foo') |
236 | element = self.wait_until_visible('#hintError-initialChar-sstate_dir', poll=2) | 188 | element = self.wait_until_visible('#hintError-initialChar-sstate_dir') |
237 | 189 | ||
238 | msg = 'sstate directory path starts with invalid character but ' \ | 190 | msg = 'sstate directory path starts with invalid character but ' \ |
239 | 'treated as valid' | 191 | 'treated as valid' |
@@ -243,7 +195,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
243 | self.driver.find_element(By.ID, 'new-sstate_dir').clear() | 195 | self.driver.find_element(By.ID, 'new-sstate_dir').clear() |
244 | self.enter_text('#new-sstate_dir', '/foo/bar a') | 196 | self.enter_text('#new-sstate_dir', '/foo/bar a') |
245 | 197 | ||
246 | element = self.wait_until_visible('#hintError-sstate_dir', poll=2) | 198 | element = self.wait_until_visible('#hintError-sstate_dir') |
247 | msg = 'sstate directory path characters invalid but treated as valid' | 199 | msg = 'sstate directory path characters invalid but treated as valid' |
248 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | 200 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) |
249 | 201 | ||
@@ -251,7 +203,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
251 | self.driver.find_element(By.ID,'new-sstate_dir').clear() | 203 | self.driver.find_element(By.ID,'new-sstate_dir').clear() |
252 | self.enter_text('#new-sstate_dir', '${TOPDIR}/down foo') | 204 | self.enter_text('#new-sstate_dir', '${TOPDIR}/down foo') |
253 | 205 | ||
254 | element = self.wait_until_visible('#hintError-sstate_dir', poll=2) | 206 | element = self.wait_until_visible('#hintError-sstate_dir') |
255 | msg = 'sstate directory path characters invalid but treated as valid' | 207 | msg = 'sstate directory path characters invalid but treated as valid' |
256 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | 208 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) |
257 | 209 | ||
@@ -275,13 +227,14 @@ class TestProjectConfig(SeleniumFunctionalTestCase): | |||
275 | var_name, field, btn_id, input_id, value, save_btn, *_ = kwargs.values() | 227 | var_name, field, btn_id, input_id, value, save_btn, *_ = kwargs.values() |
276 | """ Change bitbake variable value """ | 228 | """ Change bitbake variable value """ |
277 | self._navigate_bbv_page() | 229 | self._navigate_bbv_page() |
278 | self.wait_until_visible(f'#{btn_id}', poll=2) | 230 | self.wait_until_visible(f'#{btn_id}') |
279 | if kwargs.get('new_variable'): | 231 | if kwargs.get('new_variable'): |
280 | self.find(f"#{btn_id}").clear() | 232 | self.find(f"#{btn_id}").clear() |
281 | self.enter_text(f"#{btn_id}", f"{var_name}") | 233 | self.enter_text(f"#{btn_id}", f"{var_name}") |
282 | else: | 234 | else: |
283 | self.click(f'#{btn_id}') | 235 | self.click(f'#{btn_id}') |
284 | self.wait_until_visible(f'#{input_id}', poll=2) | 236 | |
237 | self.wait_until_visible(f'#{input_id}') | ||
285 | 238 | ||
286 | if kwargs.get('is_select'): | 239 | if kwargs.get('is_select'): |
287 | select = Select(self.find(f'#{input_id}')) | 240 | select = Select(self.find(f'#{input_id}')) |
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page.py b/bitbake/lib/toaster/tests/functional/test_project_page.py index adbe3587e4..429d86feba 100644 --- a/bitbake/lib/toaster/tests/functional/test_project_page.py +++ b/bitbake/lib/toaster/tests/functional/test_project_page.py | |||
@@ -7,8 +7,8 @@ | |||
7 | # | 7 | # |
8 | 8 | ||
9 | import os | 9 | import os |
10 | import random | ||
11 | import string | 10 | import string |
11 | import time | ||
12 | from unittest import skip | 12 | from unittest import skip |
13 | import pytest | 13 | import pytest |
14 | from django.urls import reverse | 14 | from django.urls import reverse |
@@ -22,58 +22,17 @@ from selenium.webdriver.common.by import By | |||
22 | 22 | ||
23 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled | 23 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled |
24 | 24 | ||
25 | 25 | class TestProjectPageBase(SeleniumFunctionalTestCase): | |
26 | @pytest.mark.django_db | ||
27 | @pytest.mark.order("last") | ||
28 | class TestProjectPage(SeleniumFunctionalTestCase): | ||
29 | project_id = None | 26 | project_id = None |
30 | PROJECT_NAME = 'TestProjectPage' | 27 | PROJECT_NAME = 'TestProjectPage' |
31 | 28 | ||
32 | def _create_project(self, project_name): | ||
33 | """ Create/Test new project using: | ||
34 | - Project Name: Any string | ||
35 | - Release: Any string | ||
36 | - Merge Toaster settings: True or False | ||
37 | """ | ||
38 | self.get(reverse('newproject')) | ||
39 | self.wait_until_visible('#new-project-name') | ||
40 | self.find("#new-project-name").send_keys(project_name) | ||
41 | select = Select(self.find("#projectversion")) | ||
42 | select.select_by_value('3') | ||
43 | |||
44 | # check merge toaster settings | ||
45 | checkbox = self.find('.checkbox-mergeattr') | ||
46 | if not checkbox.is_selected(): | ||
47 | checkbox.click() | ||
48 | |||
49 | if self.PROJECT_NAME != 'TestProjectPage': | ||
50 | # Reset project name if it's not the default one | ||
51 | self.PROJECT_NAME = 'TestProjectPage' | ||
52 | |||
53 | self.find("#create-project-button").click() | ||
54 | |||
55 | try: | ||
56 | self.wait_until_visible('#hint-error-project-name') | ||
57 | url = reverse('project', args=(TestProjectPage.project_id, )) | ||
58 | self.get(url) | ||
59 | self.wait_until_visible('#config-nav', poll=3) | ||
60 | except TimeoutException: | ||
61 | self.wait_until_visible('#config-nav', poll=3) | ||
62 | |||
63 | def _random_string(self, length): | ||
64 | return ''.join( | ||
65 | random.choice(string.ascii_letters) for _ in range(length) | ||
66 | ) | ||
67 | |||
68 | def _navigate_to_project_page(self): | 29 | def _navigate_to_project_page(self): |
69 | # Navigate to project page | 30 | # Navigate to project page |
70 | if TestProjectPage.project_id is None: | 31 | if TestProjectPageBase.project_id is None: |
71 | self._create_project(project_name=self._random_string(10)) | 32 | TestProjectPageBase.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True) |
72 | current_url = self.driver.current_url | 33 | |
73 | TestProjectPage.project_id = get_projectId_from_url(current_url) | 34 | url = reverse('project', args=(TestProjectPageBase.project_id,)) |
74 | else: | 35 | self.get(url) |
75 | url = reverse('project', args=(TestProjectPage.project_id,)) | ||
76 | self.get(url) | ||
77 | self.wait_until_visible('#config-nav') | 36 | self.wait_until_visible('#config-nav') |
78 | 37 | ||
79 | def _get_create_builds(self, **kwargs): | 38 | def _get_create_builds(self, **kwargs): |
@@ -81,14 +40,14 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
81 | # parameters for builds to associate with the projects | 40 | # parameters for builds to associate with the projects |
82 | now = timezone.now() | 41 | now = timezone.now() |
83 | self.project1_build_success = { | 42 | self.project1_build_success = { |
84 | 'project': Project.objects.get(id=TestProjectPage.project_id), | 43 | 'project': Project.objects.get(id=TestProjectPageBase.project_id), |
85 | 'started_on': now, | 44 | 'started_on': now, |
86 | 'completed_on': now, | 45 | 'completed_on': now, |
87 | 'outcome': Build.SUCCEEDED | 46 | 'outcome': Build.SUCCEEDED |
88 | } | 47 | } |
89 | 48 | ||
90 | self.project1_build_failure = { | 49 | self.project1_build_failure = { |
91 | 'project': Project.objects.get(id=TestProjectPage.project_id), | 50 | 'project': Project.objects.get(id=TestProjectPageBase.project_id), |
92 | 'started_on': now, | 51 | 'started_on': now, |
93 | 'completed_on': now, | 52 | 'completed_on': now, |
94 | 'outcome': Build.FAILED | 53 | 'outcome': Build.FAILED |
@@ -133,7 +92,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
133 | list_check_box_id: list | 92 | list_check_box_id: list |
134 | ): | 93 | ): |
135 | # Check edit column | 94 | # Check edit column |
136 | edit_column = self.find(f'#{edit_btn_id}') | 95 | finder = lambda driver: self.find(f'#{edit_btn_id}') |
96 | edit_column = self.wait_until_element_clickable(finder) | ||
137 | self.assertTrue(edit_column.is_displayed()) | 97 | self.assertTrue(edit_column.is_displayed()) |
138 | edit_column.click() | 98 | edit_column.click() |
139 | # Check dropdown is visible | 99 | # Check dropdown is visible |
@@ -192,7 +152,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
192 | def test_show_rows(row_to_show, show_row_link): | 152 | def test_show_rows(row_to_show, show_row_link): |
193 | # Check that we can show rows == row_to_show | 153 | # Check that we can show rows == row_to_show |
194 | show_row_link.select_by_value(str(row_to_show)) | 154 | show_row_link.select_by_value(str(row_to_show)) |
195 | self.wait_until_visible(f'#{table_selector} tbody tr', poll=3) | 155 | self.wait_until_visible(f'#{table_selector} tbody tr') |
196 | # check at least some rows are visible | 156 | # check at least some rows are visible |
197 | self.assertTrue( | 157 | self.assertTrue( |
198 | len(self.find_all(f'#{table_selector} tbody tr')) > 0 | 158 | len(self.find_all(f'#{table_selector} tbody tr')) > 0 |
@@ -222,34 +182,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
222 | rows = self.find_all(f'#{table_selector} tbody tr') | 182 | rows = self.find_all(f'#{table_selector} tbody tr') |
223 | self.assertTrue(len(rows) > 0) | 183 | self.assertTrue(len(rows) > 0) |
224 | 184 | ||
225 | def test_create_project(self): | 185 | class TestProjectPage(TestProjectPageBase): |
226 | """ Create/Test new project using: | ||
227 | - Project Name: Any string | ||
228 | - Release: Any string | ||
229 | - Merge Toaster settings: True or False | ||
230 | """ | ||
231 | self._create_project(project_name=self.PROJECT_NAME) | ||
232 | |||
233 | def test_image_recipe_editColumn(self): | ||
234 | """ Test the edit column feature in image recipe table on project page """ | ||
235 | self._get_create_builds(success=10, failure=10) | ||
236 | |||
237 | url = reverse('projectimagerecipes', args=(TestProjectPage.project_id,)) | ||
238 | self.get(url) | ||
239 | self.wait_until_present('#imagerecipestable tbody tr') | ||
240 | |||
241 | column_list = [ | ||
242 | 'get_description_or_summary', 'layer_version__get_vcs_reference', | ||
243 | 'layer_version__layer__name', 'license', 'recipe-file', 'section', | ||
244 | 'version' | ||
245 | ] | ||
246 | |||
247 | # Check that we can hide the edit column | ||
248 | self._mixin_test_table_edit_column( | ||
249 | 'imagerecipestable', | ||
250 | 'edit-columns-button', | ||
251 | [f'checkbox-{column}' for column in column_list] | ||
252 | ) | ||
253 | 186 | ||
254 | def test_page_header_on_project_page(self): | 187 | def test_page_header_on_project_page(self): |
255 | """ Check page header in project page: | 188 | """ Check page header in project page: |
@@ -272,8 +205,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
272 | logo_img = logo.find_element(By.TAG_NAME, 'img') | 205 | logo_img = logo.find_element(By.TAG_NAME, 'img') |
273 | self.assertTrue(logo_img.is_displayed(), | 206 | self.assertTrue(logo_img.is_displayed(), |
274 | 'Logo of Yocto project not found') | 207 | 'Logo of Yocto project not found') |
275 | self.assertTrue( | 208 | self.assertIn( |
276 | '/static/img/logo.png' in str(logo_img.get_attribute('src')), | 209 | '/static/img/logo.png', str(logo_img.get_attribute('src')), |
277 | 'Logo of Yocto project not found' | 210 | 'Logo of Yocto project not found' |
278 | ) | 211 | ) |
279 | # "Toaster"+" Information icon", clickable | 212 | # "Toaster"+" Information icon", clickable |
@@ -282,34 +215,34 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
282 | "//div[@class='toaster-navbar-brand']//a[@class='brand']", | 215 | "//div[@class='toaster-navbar-brand']//a[@class='brand']", |
283 | ) | 216 | ) |
284 | self.assertTrue(toaster.is_displayed(), 'Toaster not found') | 217 | self.assertTrue(toaster.is_displayed(), 'Toaster not found') |
285 | self.assertTrue(toaster.text == 'Toaster') | 218 | self.assertEqual(toaster.text, 'Toaster') |
286 | info_sign = self.find('.glyphicon-info-sign') | 219 | info_sign = self.find('.glyphicon-info-sign') |
287 | self.assertTrue(info_sign.is_displayed()) | 220 | self.assertTrue(info_sign.is_displayed()) |
288 | 221 | ||
289 | # "Server Icon" + "All builds" | 222 | # "Server Icon" + "All builds" |
290 | all_builds = self.find('#navbar-all-builds') | 223 | all_builds = self.find('#navbar-all-builds') |
291 | all_builds_link = all_builds.find_element(By.TAG_NAME, 'a') | 224 | all_builds_link = all_builds.find_element(By.TAG_NAME, 'a') |
292 | self.assertTrue("All builds" in all_builds_link.text) | 225 | self.assertIn("All builds", all_builds_link.text) |
293 | self.assertTrue( | 226 | self.assertIn( |
294 | '/toastergui/builds/' in str(all_builds_link.get_attribute('href')) | 227 | '/toastergui/builds/', str(all_builds_link.get_attribute('href')) |
295 | ) | 228 | ) |
296 | server_icon = all_builds.find_element(By.TAG_NAME, 'i') | 229 | server_icon = all_builds.find_element(By.TAG_NAME, 'i') |
297 | self.assertTrue( | 230 | self.assertEqual( |
298 | server_icon.get_attribute('class') == 'glyphicon glyphicon-tasks' | 231 | server_icon.get_attribute('class'), 'glyphicon glyphicon-tasks' |
299 | ) | 232 | ) |
300 | self.assertTrue(server_icon.is_displayed()) | 233 | self.assertTrue(server_icon.is_displayed()) |
301 | 234 | ||
302 | # "Directory Icon" + "All projects" | 235 | # "Directory Icon" + "All projects" |
303 | all_projects = self.find('#navbar-all-projects') | 236 | all_projects = self.find('#navbar-all-projects') |
304 | all_projects_link = all_projects.find_element(By.TAG_NAME, 'a') | 237 | all_projects_link = all_projects.find_element(By.TAG_NAME, 'a') |
305 | self.assertTrue("All projects" in all_projects_link.text) | 238 | self.assertIn("All projects", all_projects_link.text) |
306 | self.assertTrue( | 239 | self.assertIn( |
307 | '/toastergui/projects/' in str(all_projects_link.get_attribute( | 240 | '/toastergui/projects/', str(all_projects_link.get_attribute( |
308 | 'href')) | 241 | 'href')) |
309 | ) | 242 | ) |
310 | dir_icon = all_projects.find_element(By.TAG_NAME, 'i') | 243 | dir_icon = all_projects.find_element(By.TAG_NAME, 'i') |
311 | self.assertTrue( | 244 | self.assertEqual( |
312 | dir_icon.get_attribute('class') == 'icon-folder-open' | 245 | dir_icon.get_attribute('class'), 'icon-folder-open' |
313 | ) | 246 | ) |
314 | self.assertTrue(dir_icon.is_displayed()) | 247 | self.assertTrue(dir_icon.is_displayed()) |
315 | 248 | ||
@@ -317,23 +250,23 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
317 | toaster_docs_link = self.find('#navbar-docs') | 250 | toaster_docs_link = self.find('#navbar-docs') |
318 | toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME, | 251 | toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME, |
319 | 'a') | 252 | 'a') |
320 | self.assertTrue("Documentation" in toaster_docs_link_link.text) | 253 | self.assertIn("Documentation", toaster_docs_link_link.text) |
321 | self.assertTrue( | 254 | self.assertEqual( |
322 | toaster_docs_link_link.get_attribute('href') == 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual' | 255 | toaster_docs_link_link.get_attribute('href'), 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual' |
323 | ) | 256 | ) |
324 | book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i') | 257 | book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i') |
325 | self.assertTrue( | 258 | self.assertEqual( |
326 | book_icon.get_attribute('class') == 'glyphicon glyphicon-book' | 259 | book_icon.get_attribute('class'), 'glyphicon glyphicon-book' |
327 | ) | 260 | ) |
328 | self.assertTrue(book_icon.is_displayed()) | 261 | self.assertTrue(book_icon.is_displayed()) |
329 | 262 | ||
330 | # AT RIGHT -> button "New project" | 263 | # AT RIGHT -> button "New project" |
331 | new_project_button = self.find('#new-project-button') | 264 | new_project_button = self.find('#new-project-button') |
332 | self.assertTrue(new_project_button.is_displayed()) | 265 | self.assertTrue(new_project_button.is_displayed()) |
333 | self.assertTrue(new_project_button.text == 'New project') | 266 | self.assertEqual(new_project_button.text, 'New project') |
334 | new_project_button.click() | 267 | new_project_button.click() |
335 | self.assertTrue( | 268 | self.assertIn( |
336 | '/toastergui/newproject/' in str(self.driver.current_url) | 269 | '/toastergui/newproject/', str(self.driver.current_url) |
337 | ) | 270 | ) |
338 | 271 | ||
339 | def test_edit_project_name(self): | 272 | def test_edit_project_name(self): |
@@ -348,7 +281,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
348 | 281 | ||
349 | # click on "Edit" icon button | 282 | # click on "Edit" icon button |
350 | self.wait_until_visible('#project-name-container') | 283 | self.wait_until_visible('#project-name-container') |
351 | edit_button = self.find('#project-change-form-toggle') | 284 | finder = lambda driver: self.find('#project-change-form-toggle') |
285 | edit_button = self.wait_until_element_clickable(finder) | ||
352 | edit_button.click() | 286 | edit_button.click() |
353 | project_name_input = self.find('#project-name-change-input') | 287 | project_name_input = self.find('#project-name-change-input') |
354 | self.assertTrue(project_name_input.is_displayed()) | 288 | self.assertTrue(project_name_input.is_displayed()) |
@@ -358,8 +292,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
358 | 292 | ||
359 | # check project name is changed | 293 | # check project name is changed |
360 | self.wait_until_visible('#project-name-container') | 294 | self.wait_until_visible('#project-name-container') |
361 | self.assertTrue( | 295 | self.assertIn( |
362 | 'New Name' in str(self.find('#project-name-container').text) | 296 | 'New Name', str(self.find('#project-name-container').text) |
363 | ) | 297 | ) |
364 | 298 | ||
365 | def test_project_page_tabs(self): | 299 | def test_project_page_tabs(self): |
@@ -376,10 +310,10 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
376 | # check "configuration" tab | 310 | # check "configuration" tab |
377 | self.wait_until_visible('#topbar-configuration-tab') | 311 | self.wait_until_visible('#topbar-configuration-tab') |
378 | config_tab = self.find('#topbar-configuration-tab') | 312 | config_tab = self.find('#topbar-configuration-tab') |
379 | self.assertTrue(config_tab.get_attribute('class') == 'active') | 313 | self.assertEqual(config_tab.get_attribute('class'), 'active') |
380 | self.assertTrue('Configuration' in str(config_tab.text)) | 314 | self.assertIn('Configuration', str(config_tab.text)) |
381 | self.assertTrue( | 315 | self.assertIn( |
382 | f"/toastergui/project/{TestProjectPage.project_id}" in str(self.driver.current_url) | 316 | f"/toastergui/project/{TestProjectPageBase.project_id}", str(self.driver.current_url) |
383 | ) | 317 | ) |
384 | 318 | ||
385 | def get_tabs(): | 319 | def get_tabs(): |
@@ -392,9 +326,9 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
392 | def check_tab_link(tab_index, tab_name, url): | 326 | def check_tab_link(tab_index, tab_name, url): |
393 | tab = get_tabs()[tab_index] | 327 | tab = get_tabs()[tab_index] |
394 | tab_link = tab.find_element(By.TAG_NAME, 'a') | 328 | tab_link = tab.find_element(By.TAG_NAME, 'a') |
395 | self.assertTrue(url in tab_link.get_attribute('href')) | 329 | self.assertIn(url, tab_link.get_attribute('href')) |
396 | self.assertTrue(tab_name in tab_link.text) | 330 | self.assertIn(tab_name, tab_link.text) |
397 | self.assertTrue(tab.get_attribute('class') == 'active') | 331 | self.assertEqual(tab.get_attribute('class'), 'active') |
398 | 332 | ||
399 | # check "Builds" tab | 333 | # check "Builds" tab |
400 | builds_tab = get_tabs()[1] | 334 | builds_tab = get_tabs()[1] |
@@ -402,7 +336,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
402 | check_tab_link( | 336 | check_tab_link( |
403 | 1, | 337 | 1, |
404 | 'Builds', | 338 | 'Builds', |
405 | f"/toastergui/project/{TestProjectPage.project_id}/builds" | 339 | f"/toastergui/project/{TestProjectPageBase.project_id}/builds" |
406 | ) | 340 | ) |
407 | 341 | ||
408 | # check "Import layers" tab | 342 | # check "Import layers" tab |
@@ -411,7 +345,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
411 | check_tab_link( | 345 | check_tab_link( |
412 | 2, | 346 | 2, |
413 | 'Import layer', | 347 | 'Import layer', |
414 | f"/toastergui/project/{TestProjectPage.project_id}/importlayer" | 348 | f"/toastergui/project/{TestProjectPageBase.project_id}/importlayer" |
415 | ) | 349 | ) |
416 | 350 | ||
417 | # check "New custom image" tab | 351 | # check "New custom image" tab |
@@ -420,7 +354,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
420 | check_tab_link( | 354 | check_tab_link( |
421 | 3, | 355 | 3, |
422 | 'New custom image', | 356 | 'New custom image', |
423 | f"/toastergui/project/{TestProjectPage.project_id}/newcustomimage" | 357 | f"/toastergui/project/{TestProjectPageBase.project_id}/newcustomimage" |
424 | ) | 358 | ) |
425 | 359 | ||
426 | # check search box can be use to build recipes | 360 | # check search box can be use to build recipes |
@@ -428,13 +362,17 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
428 | search_box.send_keys('core-image-minimal') | 362 | search_box.send_keys('core-image-minimal') |
429 | self.find('#build-button').click() | 363 | self.find('#build-button').click() |
430 | self.wait_until_visible('#latest-builds') | 364 | self.wait_until_visible('#latest-builds') |
431 | lastest_builds = self.driver.find_elements( | 365 | buildtext = "Loading" |
432 | By.XPATH, | 366 | while "Loading" in buildtext: |
433 | '//div[@id="latest-builds"]', | 367 | time.sleep(1) |
434 | ) | 368 | lastest_builds = self.driver.find_elements( |
435 | last_build = lastest_builds[0] | 369 | By.XPATH, |
436 | self.assertTrue( | 370 | '//div[@id="latest-builds"]', |
437 | 'core-image-minimal' in str(last_build.text) | 371 | ) |
372 | last_build = lastest_builds[0] | ||
373 | buildtext = last_build.text | ||
374 | self.assertIn( | ||
375 | 'core-image-minimal', str(last_build.text) | ||
438 | ) | 376 | ) |
439 | 377 | ||
440 | def test_softwareRecipe_page(self): | 378 | def test_softwareRecipe_page(self): |
@@ -446,7 +384,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
446 | """ | 384 | """ |
447 | self._navigate_to_config_nav('softwarerecipestable', 4) | 385 | self._navigate_to_config_nav('softwarerecipestable', 4) |
448 | # check title "Compatible software recipes" is displayed | 386 | # check title "Compatible software recipes" is displayed |
449 | self.assertTrue("Compatible software recipes" in self.get_page_source()) | 387 | self.assertIn("Compatible software recipes", self.get_page_source()) |
450 | # Test search input | 388 | # Test search input |
451 | self._mixin_test_table_search_input( | 389 | self._mixin_test_table_search_input( |
452 | input_selector='search-input-softwarerecipestable', | 390 | input_selector='search-input-softwarerecipestable', |
@@ -455,12 +393,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
455 | table_selector='softwarerecipestable' | 393 | table_selector='softwarerecipestable' |
456 | ) | 394 | ) |
457 | # check "build recipe" button works | 395 | # check "build recipe" button works |
458 | rows = self.find_all('#softwarerecipestable tbody tr') | 396 | finder = lambda driver: self.find_all('#softwarerecipestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a') |
459 | image_to_build = rows[0] | 397 | build_btn = self.wait_until_element_clickable(finder) |
460 | build_btn = image_to_build.find_element( | ||
461 | By.XPATH, | ||
462 | '//td[@class="add-del-layers"]//a[1]' | ||
463 | ) | ||
464 | build_btn.click() | 398 | build_btn.click() |
465 | build_state = wait_until_build(self, 'queued cloning starting parsing failed') | 399 | build_state = wait_until_build(self, 'queued cloning starting parsing failed') |
466 | lastest_builds = self.driver.find_elements( | 400 | lastest_builds = self.driver.find_elements( |
@@ -468,11 +402,10 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
468 | '//div[@id="latest-builds"]/div' | 402 | '//div[@id="latest-builds"]/div' |
469 | ) | 403 | ) |
470 | self.assertTrue(len(lastest_builds) > 0) | 404 | self.assertTrue(len(lastest_builds) > 0) |
471 | last_build = lastest_builds[0] | 405 | # Find the latest builds, the last build and then the cancel button |
472 | cancel_button = last_build.find_element( | 406 | |
473 | By.XPATH, | 407 | finder = lambda driver: driver.find_elements(By.XPATH, '//div[@id="latest-builds"]/div')[0].find_element(By.XPATH, '//span[@class="cancel-build-btn pull-right alert-link"]') |
474 | '//span[@class="cancel-build-btn pull-right alert-link"]', | 408 | cancel_button = self.wait_until_element_clickable(finder) |
475 | ) | ||
476 | cancel_button.click() | 409 | cancel_button.click() |
477 | if 'starting' not in build_state: # change build state when cancelled in starting state | 410 | if 'starting' not in build_state: # change build state when cancelled in starting state |
478 | wait_until_build_cancelled(self) | 411 | wait_until_build_cancelled(self) |
@@ -510,7 +443,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
510 | """ | 443 | """ |
511 | self._navigate_to_config_nav('machinestable', 5) | 444 | self._navigate_to_config_nav('machinestable', 5) |
512 | # check title "Compatible software recipes" is displayed | 445 | # check title "Compatible software recipes" is displayed |
513 | self.assertTrue("Compatible machines" in self.get_page_source()) | 446 | self.assertIn("Compatible machines", self.get_page_source()) |
514 | # Test search input | 447 | # Test search input |
515 | self._mixin_test_table_search_input( | 448 | self._mixin_test_table_search_input( |
516 | input_selector='search-input-machinestable', | 449 | input_selector='search-input-machinestable', |
@@ -519,17 +452,13 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
519 | table_selector='machinestable' | 452 | table_selector='machinestable' |
520 | ) | 453 | ) |
521 | # check "Select machine" button works | 454 | # check "Select machine" button works |
522 | rows = self.find_all('#machinestable tbody tr') | 455 | finder = lambda driver: self.find_all('#machinestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]') |
523 | machine_to_select = rows[0] | 456 | select_btn = self.wait_until_element_clickable(finder) |
524 | select_btn = machine_to_select.find_element( | 457 | select_btn.click() |
525 | By.XPATH, | 458 | self.wait_until_visible('#project-machine-name') |
526 | '//td[@class="add-del-layers"]//a[1]' | ||
527 | ) | ||
528 | select_btn.send_keys(Keys.RETURN) | ||
529 | self.wait_until_visible('#config-nav') | ||
530 | project_machine_name = self.find('#project-machine-name') | 459 | project_machine_name = self.find('#project-machine-name') |
531 | self.assertTrue( | 460 | self.assertIn( |
532 | 'qemux86-64' in project_machine_name.text | 461 | 'qemux86-64', project_machine_name.text |
533 | ) | 462 | ) |
534 | # check "Add layer" button works | 463 | # check "Add layer" button works |
535 | self._navigate_to_config_nav('machinestable', 5) | 464 | self._navigate_to_config_nav('machinestable', 5) |
@@ -540,16 +469,23 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
540 | searchBtn_selector='search-submit-machinestable', | 469 | searchBtn_selector='search-submit-machinestable', |
541 | table_selector='machinestable' | 470 | table_selector='machinestable' |
542 | ) | 471 | ) |
543 | self.wait_until_visible('#machinestable tbody tr', poll=3) | 472 | |
544 | rows = self.find_all('#machinestable tbody tr') | 473 | self.wait_until_visible('#machinestable tbody tr') |
545 | machine_to_add = rows[0] | 474 | # Locate a machine to add button |
546 | add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]') | 475 | finder = lambda driver: self.find_all('#machinestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]') |
476 | add_btn = self.wait_until_element_clickable(finder) | ||
547 | add_btn.click() | 477 | add_btn.click() |
548 | self.wait_until_visible('#change-notification') | 478 | self.wait_until_visible('#change-notification') |
549 | change_notification = self.find('#change-notification') | 479 | change_notification = self.find('#change-notification') |
550 | self.assertTrue( | 480 | self.assertIn( |
551 | f'You have added 1 layer to your project' in str(change_notification.text) | 481 | f'You have added 1 layer to your project', str(change_notification.text) |
552 | ) | 482 | ) |
483 | |||
484 | finder = lambda driver: self.find('#hide-alert') | ||
485 | hide_button = self.wait_until_element_clickable(finder) | ||
486 | hide_button.click() | ||
487 | self.wait_until_not_visible('#change-notification') | ||
488 | |||
553 | # check Machine table feature(show/hide column, pagination) | 489 | # check Machine table feature(show/hide column, pagination) |
554 | self._navigate_to_config_nav('machinestable', 5) | 490 | self._navigate_to_config_nav('machinestable', 5) |
555 | column_list = [ | 491 | column_list = [ |
@@ -580,7 +516,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
580 | """ | 516 | """ |
581 | self._navigate_to_config_nav('layerstable', 6) | 517 | self._navigate_to_config_nav('layerstable', 6) |
582 | # check title "Compatible layers" is displayed | 518 | # check title "Compatible layers" is displayed |
583 | self.assertTrue("Compatible layers" in self.get_page_source()) | 519 | self.assertIn("Compatible layers", self.get_page_source()) |
584 | # Test search input | 520 | # Test search input |
585 | input_text='meta-tanowrt' | 521 | input_text='meta-tanowrt' |
586 | self._mixin_test_table_search_input( | 522 | self._mixin_test_table_search_input( |
@@ -590,42 +526,44 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
590 | table_selector='layerstable' | 526 | table_selector='layerstable' |
591 | ) | 527 | ) |
592 | # check "Add layer" button works | 528 | # check "Add layer" button works |
593 | self.wait_until_visible('#layerstable tbody tr', poll=3) | 529 | self.wait_until_visible('#layerstable tbody tr') |
594 | rows = self.find_all('#layerstable tbody tr') | 530 | finder = lambda driver: self.find_all('#layerstable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a[@data-directive="add"]') |
595 | layer_to_add = rows[0] | 531 | add_btn = self.wait_until_element_clickable(finder) |
596 | add_btn = layer_to_add.find_element( | ||
597 | By.XPATH, | ||
598 | '//td[@class="add-del-layers"]' | ||
599 | ) | ||
600 | add_btn.click() | 532 | add_btn.click() |
601 | # check modal is displayed | 533 | # check modal is displayed |
602 | self.wait_until_visible('#dependencies-modal', poll=3) | 534 | self.wait_until_visible('#dependencies-modal') |
603 | list_dependencies = self.find_all('#dependencies-list li') | 535 | list_dependencies = self.find_all('#dependencies-list li') |
604 | # click on add-layers button | 536 | # click on add-layers button |
605 | add_layers_btn = self.driver.find_element( | 537 | finder = lambda driver: self.driver.find_element(By.XPATH, '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]') |
606 | By.XPATH, | 538 | add_layers_btn = self.wait_until_element_clickable(finder) |
607 | '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]' | ||
608 | ) | ||
609 | add_layers_btn.click() | 539 | add_layers_btn.click() |
610 | self.wait_until_visible('#change-notification') | 540 | self.wait_until_visible('#change-notification') |
611 | change_notification = self.find('#change-notification') | 541 | change_notification = self.find('#change-notification') |
612 | self.assertTrue( | 542 | self.assertIn( |
613 | f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text) | 543 | f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies', str(change_notification.text) |
614 | ) | 544 | ) |
545 | |||
546 | finder = lambda driver: self.find('#hide-alert') | ||
547 | hide_button = self.wait_until_element_clickable(finder) | ||
548 | hide_button.click() | ||
549 | self.wait_until_not_visible('#change-notification') | ||
550 | |||
615 | # check "Remove layer" button works | 551 | # check "Remove layer" button works |
616 | self.wait_until_visible('#layerstable tbody tr', poll=3) | 552 | self.wait_until_visible('#layerstable tbody tr') |
617 | rows = self.find_all('#layerstable tbody tr') | 553 | finder = lambda driver: self.find_all('#layerstable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a[@data-directive="remove"]') |
618 | layer_to_remove = rows[0] | 554 | remove_btn = self.wait_until_element_clickable(finder) |
619 | remove_btn = layer_to_remove.find_element( | ||
620 | By.XPATH, | ||
621 | '//td[@class="add-del-layers"]' | ||
622 | ) | ||
623 | remove_btn.click() | 555 | remove_btn.click() |
624 | self.wait_until_visible('#change-notification', poll=2) | 556 | self.wait_until_visible('#change-notification') |
625 | change_notification = self.find('#change-notification') | 557 | change_notification = self.find('#change-notification') |
626 | self.assertTrue( | 558 | self.assertIn( |
627 | f'You have removed 1 layer from your project: {input_text}' in str(change_notification.text) | 559 | f'You have removed 1 layer from your project: {input_text}', str(change_notification.text) |
628 | ) | 560 | ) |
561 | |||
562 | finder = lambda driver: self.find('#hide-alert') | ||
563 | hide_button = self.wait_until_element_clickable(finder) | ||
564 | hide_button.click() | ||
565 | self.wait_until_not_visible('#change-notification') | ||
566 | |||
629 | # check layers table feature(show/hide column, pagination) | 567 | # check layers table feature(show/hide column, pagination) |
630 | self._navigate_to_config_nav('layerstable', 6) | 568 | self._navigate_to_config_nav('layerstable', 6) |
631 | column_list = [ | 569 | column_list = [ |
@@ -656,7 +594,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
656 | """ | 594 | """ |
657 | self._navigate_to_config_nav('distrostable', 7) | 595 | self._navigate_to_config_nav('distrostable', 7) |
658 | # check title "Compatible distros" is displayed | 596 | # check title "Compatible distros" is displayed |
659 | self.assertTrue("Compatible Distros" in self.get_page_source()) | 597 | self.assertIn("Compatible Distros", self.get_page_source()) |
660 | # Test search input | 598 | # Test search input |
661 | input_text='poky-altcfg' | 599 | input_text='poky-altcfg' |
662 | self._mixin_test_table_search_input( | 600 | self._mixin_test_table_search_input( |
@@ -666,17 +604,14 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
666 | table_selector='distrostable' | 604 | table_selector='distrostable' |
667 | ) | 605 | ) |
668 | # check "Add distro" button works | 606 | # check "Add distro" button works |
669 | rows = self.find_all('#distrostable tbody tr') | 607 | self.wait_until_visible(".add-del-layers") |
670 | distro_to_add = rows[0] | 608 | finder = lambda driver: self.find_all('#distrostable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]') |
671 | add_btn = distro_to_add.find_element( | 609 | add_btn = self.wait_until_element_clickable(finder) |
672 | By.XPATH, | ||
673 | '//td[@class="add-del-layers"]//a[1]' | ||
674 | ) | ||
675 | add_btn.click() | 610 | add_btn.click() |
676 | self.wait_until_visible('#change-notification', poll=2) | 611 | self.wait_until_visible('#change-notification') |
677 | change_notification = self.find('#change-notification') | 612 | change_notification = self.find('#change-notification') |
678 | self.assertTrue( | 613 | self.assertIn( |
679 | f'You have changed the distro to: {input_text}' in str(change_notification.text) | 614 | f'You have changed the distro to: {input_text}', str(change_notification.text) |
680 | ) | 615 | ) |
681 | # check distro table feature(show/hide column, pagination) | 616 | # check distro table feature(show/hide column, pagination) |
682 | self._navigate_to_config_nav('distrostable', 7) | 617 | self._navigate_to_config_nav('distrostable', 7) |
@@ -699,7 +634,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
699 | ) | 634 | ) |
700 | 635 | ||
701 | def test_single_layer_page(self): | 636 | def test_single_layer_page(self): |
702 | """ Test layer page | 637 | """ Test layer details page using meta-poky as an example (assumes is added to start with) |
703 | - Check if title is displayed | 638 | - Check if title is displayed |
704 | - Check add/remove layer button works | 639 | - Check add/remove layer button works |
705 | - Check tabs(layers, recipes, machines) are displayed | 640 | - Check tabs(layers, recipes, machines) are displayed |
@@ -708,45 +643,62 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
708 | - Check layer summary | 643 | - Check layer summary |
709 | - Check layer description | 644 | - Check layer description |
710 | """ | 645 | """ |
711 | url = reverse("layerdetails", args=(TestProjectPage.project_id, 8)) | 646 | self._navigate_to_config_nav('layerstable', 6) |
712 | self.get(url) | 647 | layer_link = self.driver.find_element(By.XPATH, '//tr/td[@class="layer__name"]/a[contains(text(),"meta-poky")]') |
648 | layer_link.click() | ||
713 | self.wait_until_visible('.page-header') | 649 | self.wait_until_visible('.page-header') |
714 | # check title is displayed | 650 | # check title is displayed |
715 | self.assertTrue(self.find('.page-header h1').is_displayed()) | 651 | self.assertTrue(self.find('.page-header h1').is_displayed()) |
716 | 652 | ||
717 | # check add layer button works | 653 | # check remove layer button works |
718 | remove_layer_btn = self.find('#add-remove-layer-btn') | 654 | finder = lambda driver: self.find('#add-remove-layer-btn') |
655 | remove_layer_btn = self.wait_until_element_clickable(finder) | ||
719 | remove_layer_btn.click() | 656 | remove_layer_btn.click() |
720 | self.wait_until_visible('#change-notification', poll=2) | 657 | self.wait_until_visible('#change-notification') |
721 | change_notification = self.find('#change-notification') | 658 | change_notification = self.find('#change-notification') |
722 | self.assertTrue( | 659 | self.assertIn( |
723 | f'You have removed 1 layer from your project' in str(change_notification.text) | 660 | f'You have removed 1 layer from your project', str(change_notification.text) |
724 | ) | 661 | ) |
725 | # check add layer button works, 18 is the random layer id | 662 | finder = lambda driver: self.find('#hide-alert') |
726 | add_layer_btn = self.find('#add-remove-layer-btn') | 663 | hide_button = self.wait_until_element_clickable(finder) |
664 | hide_button.click() | ||
665 | # check add layer button works | ||
666 | self.wait_until_not_visible('#change-notification') | ||
667 | finder = lambda driver: self.find('#add-remove-layer-btn') | ||
668 | add_layer_btn = self.wait_until_element_clickable(finder) | ||
727 | add_layer_btn.click() | 669 | add_layer_btn.click() |
728 | self.wait_until_visible('#change-notification') | 670 | self.wait_until_visible('#change-notification') |
729 | change_notification = self.find('#change-notification') | 671 | change_notification = self.find('#change-notification') |
730 | self.assertTrue( | 672 | self.assertIn( |
731 | f'You have added 1 layer to your project' in str(change_notification.text) | 673 | f'You have added 1 layer to your project', str(change_notification.text) |
732 | ) | 674 | ) |
675 | finder = lambda driver: self.find('#hide-alert') | ||
676 | hide_button = self.wait_until_element_clickable(finder) | ||
677 | hide_button.click() | ||
678 | self.wait_until_not_visible('#change-notification') | ||
733 | # check tabs(layers, recipes, machines) are displayed | 679 | # check tabs(layers, recipes, machines) are displayed |
734 | tabs = self.find_all('.nav-tabs li') | 680 | tabs = self.find_all('.nav-tabs li') |
735 | self.assertEqual(len(tabs), 3) | 681 | self.assertEqual(len(tabs), 3) |
736 | # Check first tab | 682 | # Check first tab |
737 | tabs[0].click() | 683 | tabs[0].click() |
738 | self.assertTrue( | 684 | self.assertIn( |
739 | 'active' in str(self.find('#information').get_attribute('class')) | 685 | 'active', str(self.find('#information').get_attribute('class')) |
740 | ) | 686 | ) |
741 | # Check second tab | 687 | # Check second tab (recipes) |
688 | self.wait_until_visible('.nav-tabs') | ||
689 | # Ensure page is scrolled to the top | ||
690 | self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})') | ||
742 | tabs[1].click() | 691 | tabs[1].click() |
743 | self.assertTrue( | 692 | self.assertIn( |
744 | 'active' in str(self.find('#recipes').get_attribute('class')) | 693 | 'active', str(self.find('#recipes').get_attribute('class')) |
745 | ) | 694 | ) |
746 | # Check third tab | 695 | # Check third tab (machines) |
696 | self.wait_until_visible('.nav-tabs') | ||
697 | # Ensure page is scrolled to the top | ||
698 | self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})') | ||
747 | tabs[2].click() | 699 | tabs[2].click() |
748 | self.assertTrue( | 700 | self.assertIn( |
749 | 'active' in str(self.find('#machines').get_attribute('class')) | 701 | 'active', str(self.find('#machines').get_attribute('class')) |
750 | ) | 702 | ) |
751 | # Check left section is displayed | 703 | # Check left section is displayed |
752 | section = self.find('.well') | 704 | section = self.find('.well') |
@@ -755,9 +707,13 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
755 | section.find_element(By.XPATH, '//h2[1]').is_displayed() | 707 | section.find_element(By.XPATH, '//h2[1]').is_displayed() |
756 | ) | 708 | ) |
757 | # Check layer summary | 709 | # Check layer summary |
758 | self.assertTrue("Summary" in section.text) | 710 | self.assertIn("Summary", section.text) |
759 | # Check layer description | 711 | # Check layer description |
760 | self.assertTrue("Description" in section.text) | 712 | self.assertIn("Description", section.text) |
713 | |||
714 | @pytest.mark.django_db | ||
715 | @pytest.mark.order("last") | ||
716 | class TestProjectPageRecipes(TestProjectPageBase): | ||
761 | 717 | ||
762 | def test_single_recipe_page(self): | 718 | def test_single_recipe_page(self): |
763 | """ Test recipe page | 719 | """ Test recipe page |
@@ -767,7 +723,12 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
767 | - Check recipe: name, summary, description, Version, Section, | 723 | - Check recipe: name, summary, description, Version, Section, |
768 | License, Approx. packages included, Approx. size, Recipe file | 724 | License, Approx. packages included, Approx. size, Recipe file |
769 | """ | 725 | """ |
770 | url = reverse("recipedetails", args=(TestProjectPage.project_id, 53428)) | 726 | # Use a recipe which is likely to exist in the layer index but not enabled |
727 | # in poky out the box - xen-image-minimal from meta-virtualization | ||
728 | self._navigate_to_project_page() | ||
729 | prj = Project.objects.get(pk=TestProjectPageBase.project_id) | ||
730 | recipe_id = prj.get_all_compatible_recipes().get(name="xen-image-minimal").pk | ||
731 | url = reverse("recipedetails", args=(TestProjectPageBase.project_id, recipe_id)) | ||
771 | self.get(url) | 732 | self.get(url) |
772 | self.wait_until_visible('.page-header') | 733 | self.wait_until_visible('.page-header') |
773 | # check title is displayed | 734 | # check title is displayed |
@@ -782,11 +743,33 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
782 | section.find_element(By.XPATH, '//h2[1]').is_displayed() | 743 | section.find_element(By.XPATH, '//h2[1]').is_displayed() |
783 | ) | 744 | ) |
784 | # Check recipe sections details info are displayed | 745 | # Check recipe sections details info are displayed |
785 | self.assertTrue("Summary" in section.text) | 746 | self.assertIn("Summary", section.text) |
786 | self.assertTrue("Description" in section.text) | 747 | self.assertIn("Description", section.text) |
787 | self.assertTrue("Version" in section.text) | 748 | self.assertIn("Version", section.text) |
788 | self.assertTrue("Section" in section.text) | 749 | self.assertIn("Section", section.text) |
789 | self.assertTrue("License" in section.text) | 750 | self.assertIn("License", section.text) |
790 | self.assertTrue("Approx. packages included" in section.text) | 751 | self.assertIn("Approx. packages included", section.text) |
791 | self.assertTrue("Approx. package size" in section.text) | 752 | self.assertIn("Approx. package size", section.text) |
792 | self.assertTrue("Recipe file" in section.text) | 753 | self.assertIn("Recipe file", section.text) |
754 | |||
755 | def test_image_recipe_editColumn(self): | ||
756 | """ Test the edit column feature in image recipe table on project page """ | ||
757 | self._get_create_builds(success=10, failure=10) | ||
758 | |||
759 | url = reverse('projectimagerecipes', args=(TestProjectPageBase.project_id,)) | ||
760 | self.get(url) | ||
761 | self.wait_until_present('#imagerecipestable tbody tr') | ||
762 | |||
763 | column_list = [ | ||
764 | 'get_description_or_summary', 'layer_version__get_vcs_reference', | ||
765 | 'layer_version__layer__name', 'license', 'recipe-file', 'section', | ||
766 | 'version' | ||
767 | ] | ||
768 | |||
769 | # Check that we can hide the edit column | ||
770 | self._mixin_test_table_edit_column( | ||
771 | 'imagerecipestable', | ||
772 | 'edit-columns-button', | ||
773 | [f'checkbox-{column}' for column in column_list] | ||
774 | ) | ||
775 | |||
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py index eb905ddf3f..80c53e1544 100644 --- a/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py +++ b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py | |||
@@ -7,72 +7,27 @@ | |||
7 | # | 7 | # |
8 | 8 | ||
9 | import string | 9 | import string |
10 | import random | 10 | import time |
11 | import pytest | 11 | import pytest |
12 | from django.urls import reverse | 12 | from django.urls import reverse |
13 | from selenium.webdriver import Keys | 13 | from selenium.webdriver import Keys |
14 | from selenium.webdriver.support.select import Select | 14 | from selenium.webdriver.support.select import Select |
15 | from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, TimeoutException | 15 | from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, TimeoutException |
16 | from orm.models import Project | ||
17 | from tests.functional.functional_helpers import SeleniumFunctionalTestCase | 16 | from tests.functional.functional_helpers import SeleniumFunctionalTestCase |
18 | from selenium.webdriver.common.by import By | 17 | from selenium.webdriver.common.by import By |
19 | 18 | ||
20 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled | 19 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled |
21 | 20 | ||
22 | 21 | class TestProjectConfigTabBase(SeleniumFunctionalTestCase): | |
23 | @pytest.mark.django_db | ||
24 | @pytest.mark.order("last") | ||
25 | class TestProjectConfigTab(SeleniumFunctionalTestCase): | ||
26 | PROJECT_NAME = 'TestProjectConfigTab' | 22 | PROJECT_NAME = 'TestProjectConfigTab' |
27 | project_id = None | 23 | project_id = None |
28 | 24 | ||
29 | def _create_project(self, project_name, **kwargs): | ||
30 | """ Create/Test new project using: | ||
31 | - Project Name: Any string | ||
32 | - Release: Any string | ||
33 | - Merge Toaster settings: True or False | ||
34 | """ | ||
35 | release = kwargs.get('release', '3') | ||
36 | self.get(reverse('newproject')) | ||
37 | self.wait_until_visible('#new-project-name') | ||
38 | self.find("#new-project-name").send_keys(project_name) | ||
39 | select = Select(self.find("#projectversion")) | ||
40 | select.select_by_value(release) | ||
41 | |||
42 | # check merge toaster settings | ||
43 | checkbox = self.find('.checkbox-mergeattr') | ||
44 | if not checkbox.is_selected(): | ||
45 | checkbox.click() | ||
46 | |||
47 | if self.PROJECT_NAME != 'TestProjectConfigTab': | ||
48 | # Reset project name if it's not the default one | ||
49 | self.PROJECT_NAME = 'TestProjectConfigTab' | ||
50 | |||
51 | self.find("#create-project-button").click() | ||
52 | |||
53 | try: | ||
54 | self.wait_until_visible('#hint-error-project-name', poll=3) | ||
55 | url = reverse('project', args=(TestProjectConfigTab.project_id, )) | ||
56 | self.get(url) | ||
57 | self.wait_until_visible('#config-nav', poll=3) | ||
58 | except TimeoutException: | ||
59 | self.wait_until_visible('#config-nav', poll=3) | ||
60 | |||
61 | def _random_string(self, length): | ||
62 | return ''.join( | ||
63 | random.choice(string.ascii_letters) for _ in range(length) | ||
64 | ) | ||
65 | |||
66 | def _navigate_to_project_page(self): | 25 | def _navigate_to_project_page(self): |
67 | # Navigate to project page | 26 | # Navigate to project page |
68 | if TestProjectConfigTab.project_id is None: | 27 | if TestProjectConfigTabBase.project_id is None: |
69 | self._create_project(project_name=self._random_string(10)) | 28 | TestProjectConfigTabBase.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True) |
70 | current_url = self.driver.current_url | 29 | url = reverse('project', args=(TestProjectConfigTabBase.project_id,)) |
71 | TestProjectConfigTab.project_id = get_projectId_from_url( | 30 | self.get(url) |
72 | current_url) | ||
73 | else: | ||
74 | url = reverse('project', args=(TestProjectConfigTab.project_id,)) | ||
75 | self.get(url) | ||
76 | self.wait_until_visible('#config-nav') | 31 | self.wait_until_visible('#config-nav') |
77 | 32 | ||
78 | def _create_builds(self): | 33 | def _create_builds(self): |
@@ -88,8 +43,8 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
88 | '//div[@id="latest-builds"]/div', | 43 | '//div[@id="latest-builds"]/div', |
89 | ) | 44 | ) |
90 | last_build = lastest_builds[0] | 45 | last_build = lastest_builds[0] |
91 | self.assertTrue( | 46 | self.assertIn( |
92 | 'foo' in str(last_build.text) | 47 | 'foo', str(last_build.text) |
93 | ) | 48 | ) |
94 | last_build = lastest_builds[0] | 49 | last_build = lastest_builds[0] |
95 | try: | 50 | try: |
@@ -114,6 +69,8 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
114 | config_nav = self.find('#config-nav') | 69 | config_nav = self.find('#config-nav') |
115 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] | 70 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] |
116 | 71 | ||
72 | class TestProjectConfigTab(TestProjectConfigTabBase): | ||
73 | |||
117 | def test_project_config_nav(self): | 74 | def test_project_config_nav(self): |
118 | """ Test project config tab navigation: | 75 | """ Test project config tab navigation: |
119 | - Check if the menu is displayed and contains the right elements: | 76 | - Check if the menu is displayed and contains the right elements: |
@@ -138,48 +95,48 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
138 | 95 | ||
139 | def check_config_nav_item(index, item_name, url): | 96 | def check_config_nav_item(index, item_name, url): |
140 | item = _get_config_nav_item(index) | 97 | item = _get_config_nav_item(index) |
141 | self.assertTrue(item_name in item.text) | 98 | self.assertIn(item_name, item.text) |
142 | self.assertTrue(item.get_attribute('class') == 'active') | 99 | self.assertEqual(item.get_attribute('class'), 'active') |
143 | self.assertTrue(url in self.driver.current_url) | 100 | self.assertIn(url, self.driver.current_url) |
144 | 101 | ||
145 | # check if the menu contains the right elements | 102 | # check if the menu contains the right elements |
146 | # COMPATIBLE METADATA | 103 | # COMPATIBLE METADATA |
147 | compatible_metadata = _get_config_nav_item(1) | 104 | compatible_metadata = _get_config_nav_item(1) |
148 | self.assertTrue( | 105 | self.assertIn( |
149 | "compatible metadata" in compatible_metadata.text.lower() | 106 | "compatible metadata", compatible_metadata.text.lower() |
150 | ) | 107 | ) |
151 | # EXTRA CONFIGURATION | 108 | # EXTRA CONFIGURATION |
152 | extra_configuration = _get_config_nav_item(8) | 109 | extra_configuration = _get_config_nav_item(8) |
153 | self.assertTrue( | 110 | self.assertIn( |
154 | "extra configuration" in extra_configuration.text.lower() | 111 | "extra configuration", extra_configuration.text.lower() |
155 | ) | 112 | ) |
156 | # Actions | 113 | # Actions |
157 | actions = _get_config_nav_item(10) | 114 | actions = _get_config_nav_item(10) |
158 | self.assertTrue("actions" in str(actions.text).lower()) | 115 | self.assertIn("actions", str(actions.text).lower()) |
159 | 116 | ||
160 | conf_nav_list = [ | 117 | conf_nav_list = [ |
161 | # config | 118 | # config |
162 | [0, 'Configuration', | 119 | [0, 'Configuration', |
163 | f"/toastergui/project/{TestProjectConfigTab.project_id}"], | 120 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}"], |
164 | # custom images | 121 | # custom images |
165 | [2, 'Custom images', | 122 | [2, 'Custom images', |
166 | f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"], | 123 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}/customimages"], |
167 | # image recipes | 124 | # image recipes |
168 | [3, 'Image recipes', | 125 | [3, 'Image recipes', |
169 | f"/toastergui/project/{TestProjectConfigTab.project_id}/images"], | 126 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}/images"], |
170 | # software recipes | 127 | # software recipes |
171 | [4, 'Software recipes', | 128 | [4, 'Software recipes', |
172 | f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"], | 129 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}/softwarerecipes"], |
173 | # machines | 130 | # machines |
174 | [5, 'Machines', | 131 | [5, 'Machines', |
175 | f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"], | 132 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}/machines"], |
176 | # layers | 133 | # layers |
177 | [6, 'Layers', | 134 | [6, 'Layers', |
178 | f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"], | 135 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}/layers"], |
179 | # distro | 136 | # distro |
180 | [7, 'Distros', | 137 | [7, 'Distros', |
181 | f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"], | 138 | f"/toastergui/project/{TestProjectConfigTabBase.project_id}/distros"], |
182 | # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables | 139 | # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTabBase.project_id}/configuration"], # bitbake variables |
183 | ] | 140 | ] |
184 | for index, item_name, url in conf_nav_list: | 141 | for index, item_name, url in conf_nav_list: |
185 | item = _get_config_nav_item(index) | 142 | item = _get_config_nav_item(index) |
@@ -253,7 +210,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
253 | def test_show_rows(row_to_show, show_row_link): | 210 | def test_show_rows(row_to_show, show_row_link): |
254 | # Check that we can show rows == row_to_show | 211 | # Check that we can show rows == row_to_show |
255 | show_row_link.select_by_value(str(row_to_show)) | 212 | show_row_link.select_by_value(str(row_to_show)) |
256 | self.wait_until_visible('#imagerecipestable tbody tr', poll=3) | 213 | self.wait_until_visible('#imagerecipestable tbody tr') |
257 | # check at least some rows are visible | 214 | # check at least some rows are visible |
258 | self.assertTrue( | 215 | self.assertTrue( |
259 | len(self.find_all('#imagerecipestable tbody tr')) > 0 | 216 | len(self.find_all('#imagerecipestable tbody tr')) > 0 |
@@ -299,9 +256,11 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
299 | - meta-poky | 256 | - meta-poky |
300 | - meta-yocto-bsp | 257 | - meta-yocto-bsp |
301 | """ | 258 | """ |
302 | # Create a new project for this test | 259 | project_id = self.create_new_project(self.PROJECT_NAME + "-ST", '3', None, True) |
303 | project_name = self._random_string(10) | 260 | url = reverse('project', args=(project_id,)) |
304 | self._create_project(project_name=project_name) | 261 | self.get(url) |
262 | self.wait_until_visible('#config-nav') | ||
263 | |||
305 | # check if the menu is displayed | 264 | # check if the menu is displayed |
306 | self.wait_until_visible('#project-page') | 265 | self.wait_until_visible('#project-page') |
307 | block_l = self.driver.find_element( | 266 | block_l = self.driver.find_element( |
@@ -313,7 +272,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
313 | def check_machine_distro(self, item_name, new_item_name, block_id): | 272 | def check_machine_distro(self, item_name, new_item_name, block_id): |
314 | block = self.find(f'#{block_id}') | 273 | block = self.find(f'#{block_id}') |
315 | title = block.find_element(By.TAG_NAME, 'h3') | 274 | title = block.find_element(By.TAG_NAME, 'h3') |
316 | self.assertTrue(item_name.capitalize() in title.text) | 275 | self.assertIn(item_name.capitalize(), title.text) |
317 | edit_btn = self.find(f'#change-{item_name}-toggle') | 276 | edit_btn = self.find(f'#change-{item_name}-toggle') |
318 | edit_btn.click() | 277 | edit_btn.click() |
319 | self.wait_until_visible(f'#{item_name}-change-input') | 278 | self.wait_until_visible(f'#{item_name}-change-input') |
@@ -324,12 +283,15 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
324 | change_btn.click() | 283 | change_btn.click() |
325 | self.wait_until_visible(f'#project-{item_name}-name') | 284 | self.wait_until_visible(f'#project-{item_name}-name') |
326 | project_name = self.find(f'#project-{item_name}-name') | 285 | project_name = self.find(f'#project-{item_name}-name') |
327 | self.assertTrue(new_item_name in project_name.text) | 286 | self.assertIn(new_item_name, project_name.text) |
328 | # check change notificaiton is displayed | 287 | # check change notificaiton is displayed |
329 | change_notification = self.find('#change-notification') | 288 | change_notification = self.find('#change-notification') |
330 | self.assertTrue( | 289 | self.assertIn( |
331 | f'You have changed the {item_name} to: {new_item_name}' in change_notification.text | 290 | f'You have changed the {item_name} to: {new_item_name}', change_notification.text |
332 | ) | 291 | ) |
292 | hide_button = self.find('#hide-alert') | ||
293 | hide_button.click() | ||
294 | self.wait_until_not_visible('#change-notification') | ||
333 | 295 | ||
334 | # Machine | 296 | # Machine |
335 | check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section') | 297 | check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section') |
@@ -338,97 +300,51 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
338 | 300 | ||
339 | # Project release | 301 | # Project release |
340 | title = project_release.find_element(By.TAG_NAME, 'h3') | 302 | title = project_release.find_element(By.TAG_NAME, 'h3') |
341 | self.assertTrue("Project release" in title.text) | 303 | self.assertIn("Project release", title.text) |
342 | self.assertTrue( | 304 | self.assertIn( |
343 | "Yocto Project master" in self.find('#project-release-title').text | 305 | "Yocto Project master", self.find('#project-release-title').text |
344 | ) | 306 | ) |
345 | # Layers | 307 | # Layers |
346 | title = layers.find_element(By.TAG_NAME, 'h3') | 308 | title = layers.find_element(By.TAG_NAME, 'h3') |
347 | self.assertTrue("Layers" in title.text) | 309 | self.assertIn("Layers", title.text) |
310 | self.wait_until_clickable('#layer-add-input') | ||
348 | # check at least three layers are displayed | 311 | # check at least three layers are displayed |
349 | # openembedded-core | 312 | # openembedded-core |
350 | # meta-poky | 313 | # meta-poky |
351 | # meta-yocto-bsp | 314 | # meta-yocto-bsp |
352 | layers_list = layers.find_element(By.ID, 'layers-in-project-list') | 315 | layer_list_items = [] |
353 | layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') | 316 | starttime = time.time() |
317 | while len(layer_list_items) < 3: | ||
318 | layers_list = self.driver.find_element(By.ID, 'layers-in-project-list') | ||
319 | layer_list_items = layers_list.find_elements(By.TAG_NAME, 'li') | ||
320 | if time.time() > (starttime + 30): | ||
321 | self.fail("Layer list didn't contain at least 3 items within 30s (contained %d)" % len(layer_list_items)) | ||
322 | |||
354 | # remove all layers except the first three layers | 323 | # remove all layers except the first three layers |
355 | for i in range(3, len(layers_list_items)): | 324 | for i in range(3, len(layer_list_items)): |
356 | layers_list_items[i].find_element(By.TAG_NAME, 'span').click() | 325 | layer_list_items[i].find_element(By.TAG_NAME, 'span').click() |
326 | |||
357 | # check can add a layer if exists | 327 | # check can add a layer if exists |
358 | add_layer_input = layers.find_element(By.ID, 'layer-add-input') | 328 | add_layer_input = layers.find_element(By.ID, 'layer-add-input') |
359 | add_layer_input.send_keys('meta-oe') | 329 | add_layer_input.send_keys('meta-oe') |
360 | self.wait_until_visible('#layer-container > form > div > span > div') | 330 | self.wait_until_visible('#layer-container > form > div > span > div') |
361 | dropdown_item = self.driver.find_element( | 331 | self.wait_until_visible('.dropdown-menu') |
362 | By.XPATH, | 332 | finder = lambda driver: driver.find_element(By.XPATH, '//*[@id="layer-container"]/form/div/span/div/div/div') |
363 | '//*[@id="layer-container"]/form/div/span/div' | 333 | dropdown_item = self.wait_until_element_clickable(finder) |
364 | ) | 334 | dropdown_item.click() |
365 | try: | 335 | self.wait_until_clickable('#add-layer-btn') |
366 | dropdown_item.click() | ||
367 | except ElementClickInterceptedException: | ||
368 | self.skipTest( | ||
369 | "layer-container dropdown item click intercepted. Element not properly visible.") | ||
370 | add_layer_btn = layers.find_element(By.ID, 'add-layer-btn') | 336 | add_layer_btn = layers.find_element(By.ID, 'add-layer-btn') |
371 | add_layer_btn.click() | 337 | add_layer_btn.click() |
372 | self.wait_until_visible('#layers-in-project-list') | 338 | self.wait_until_visible('#layers-in-project-list') |
373 | # check layer is added | ||
374 | layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') | ||
375 | self.assertTrue(len(layers_list_items) == 4) | ||
376 | 339 | ||
377 | def test_most_build_recipes(self): | 340 | # check layer is added |
378 | """ Test most build recipes block contains""" | 341 | layer_list_items = [] |
379 | def rebuild_from_most_build_recipes(recipe_list_items): | 342 | starttime = time.time() |
380 | checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input') | 343 | while len(layer_list_items) < 4: |
381 | checkbox.click() | 344 | layers_list = self.driver.find_element(By.ID, 'layers-in-project-list') |
382 | build_btn = self.find('#freq-build-btn') | 345 | layer_list_items = layers_list.find_elements(By.TAG_NAME, 'li') |
383 | build_btn.click() | 346 | if time.time() > (starttime + 30): |
384 | self.wait_until_visible('#latest-builds') | 347 | self.fail("Layer list didn't contain at least 4 items within 30s (contained %d)" % len(layer_list_items)) |
385 | wait_until_build(self, 'queued cloning starting parsing failed') | ||
386 | lastest_builds = self.driver.find_elements( | ||
387 | By.XPATH, | ||
388 | '//div[@id="latest-builds"]/div' | ||
389 | ) | ||
390 | self.assertTrue(len(lastest_builds) >= 2) | ||
391 | last_build = lastest_builds[0] | ||
392 | try: | ||
393 | cancel_button = last_build.find_element( | ||
394 | By.XPATH, | ||
395 | '//span[@class="cancel-build-btn pull-right alert-link"]', | ||
396 | ) | ||
397 | cancel_button.click() | ||
398 | except NoSuchElementException: | ||
399 | # Skip if the build is already cancelled | ||
400 | pass | ||
401 | wait_until_build_cancelled(self) | ||
402 | # Create a new project for remaining asserts | ||
403 | project_name = self._random_string(10) | ||
404 | self._create_project(project_name=project_name, release='2') | ||
405 | current_url = self.driver.current_url | ||
406 | TestProjectConfigTab.project_id = get_projectId_from_url(current_url) | ||
407 | url = current_url.split('?')[0] | ||
408 | |||
409 | # Create a new builds | ||
410 | self._create_builds() | ||
411 | |||
412 | # back to project page | ||
413 | self.driver.get(url) | ||
414 | |||
415 | self.wait_until_visible('#project-page', poll=3) | ||
416 | |||
417 | # Most built recipes | ||
418 | most_built_recipes = self.driver.find_element( | ||
419 | By.XPATH, '//*[@id="project-page"]/div[1]/div[3]') | ||
420 | title = most_built_recipes.find_element(By.TAG_NAME, 'h3') | ||
421 | self.assertTrue("Most built recipes" in title.text) | ||
422 | # check can select a recipe and build it | ||
423 | self.wait_until_visible('#freq-build-list', poll=3) | ||
424 | recipe_list = self.find('#freq-build-list') | ||
425 | recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li') | ||
426 | self.assertTrue( | ||
427 | len(recipe_list_items) > 0, | ||
428 | msg="Any recipes found in the most built recipes list", | ||
429 | ) | ||
430 | rebuild_from_most_build_recipes(recipe_list_items) | ||
431 | TestProjectConfigTab.project_id = None # reset project id | ||
432 | 348 | ||
433 | def test_project_page_tab_importlayer(self): | 349 | def test_project_page_tab_importlayer(self): |
434 | """ Test project page tab import layer """ | 350 | """ Test project page tab import layer """ |
@@ -466,42 +382,42 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
466 | layers = block_l.find_element(By.ID, 'layer-container') | 382 | layers = block_l.find_element(By.ID, 'layer-container') |
467 | layers_list = layers.find_element(By.ID, 'layers-in-project-list') | 383 | layers_list = layers.find_element(By.ID, 'layers-in-project-list') |
468 | layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') | 384 | layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') |
469 | self.assertTrue( | 385 | self.assertIn( |
470 | 'meta-fake' in str(layers_list_items[-1].text) | 386 | 'meta-fake', str(layers_list_items[-1].text) |
471 | ) | 387 | ) |
472 | 388 | ||
473 | def test_project_page_custom_image_no_image(self): | 389 | def test_project_page_custom_image_no_image(self): |
474 | """ Test project page tab "New custom image" when no custom image """ | 390 | """ Test project page tab "New custom image" when no custom image """ |
475 | project_name = self._random_string(10) | 391 | project_id = self.create_new_project(self.PROJECT_NAME + "-CustomImage", '3', None, True) |
476 | self._create_project(project_name=project_name) | 392 | url = reverse('project', args=(project_id,)) |
477 | current_url = self.driver.current_url | 393 | self.get(url) |
478 | TestProjectConfigTab.project_id = get_projectId_from_url(current_url) | 394 | self.wait_until_visible('#config-nav') |
395 | |||
479 | # navigate to "Custom image" tab | 396 | # navigate to "Custom image" tab |
480 | custom_image_section = self._get_config_nav_item(2) | 397 | custom_image_section = self._get_config_nav_item(2) |
481 | custom_image_section.click() | 398 | custom_image_section.click() |
482 | self.wait_until_visible('#empty-state-customimagestable') | 399 | self.wait_until_visible('#empty-state-customimagestable') |
483 | 400 | ||
484 | # Check message when no custom image | 401 | # Check message when no custom image |
485 | self.assertTrue( | 402 | self.assertIn( |
486 | "You have not created any custom images yet." in str( | 403 | "You have not created any custom images yet.", str( |
487 | self.find('#empty-state-customimagestable').text | 404 | self.find('#empty-state-customimagestable').text |
488 | ) | 405 | ) |
489 | ) | 406 | ) |
490 | div_empty_msg = self.find('#empty-state-customimagestable') | 407 | div_empty_msg = self.find('#empty-state-customimagestable') |
491 | link_create_custom_image = div_empty_msg.find_element( | 408 | link_create_custom_image = div_empty_msg.find_element( |
492 | By.TAG_NAME, 'a') | 409 | By.TAG_NAME, 'a') |
493 | self.assertTrue(TestProjectConfigTab.project_id is not None) | 410 | self.assertTrue(TestProjectConfigTabBase.project_id is not None) |
494 | self.assertTrue( | 411 | self.assertIn( |
495 | f"/toastergui/project/{TestProjectConfigTab.project_id}/newcustomimage" in str( | 412 | f"/toastergui/project/{project_id}/newcustomimage", str( |
496 | link_create_custom_image.get_attribute('href') | 413 | link_create_custom_image.get_attribute('href') |
497 | ) | 414 | ) |
498 | ) | 415 | ) |
499 | self.assertTrue( | 416 | self.assertIn( |
500 | "Create your first custom image" in str( | 417 | "Create your first custom image", str( |
501 | link_create_custom_image.text | 418 | link_create_custom_image.text |
502 | ) | 419 | ) |
503 | ) | 420 | ) |
504 | TestProjectConfigTab.project_id = None # reset project id | ||
505 | 421 | ||
506 | def test_project_page_image_recipe(self): | 422 | def test_project_page_image_recipe(self): |
507 | """ Test project page section images | 423 | """ Test project page section images |
@@ -526,3 +442,66 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase): | |||
526 | self.wait_until_visible('#imagerecipestable tbody tr') | 442 | self.wait_until_visible('#imagerecipestable tbody tr') |
527 | rows = self.find_all('#imagerecipestable tbody tr') | 443 | rows = self.find_all('#imagerecipestable tbody tr') |
528 | self.assertTrue(len(rows) > 0) | 444 | self.assertTrue(len(rows) > 0) |
445 | |||
446 | @pytest.mark.django_db | ||
447 | @pytest.mark.order("last") | ||
448 | class TestProjectConfigTabDB(TestProjectConfigTabBase): | ||
449 | |||
450 | def test_most_build_recipes(self): | ||
451 | """ Test most build recipes block contains""" | ||
452 | def rebuild_from_most_build_recipes(recipe_list_items): | ||
453 | checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input') | ||
454 | checkbox.click() | ||
455 | build_btn = self.find('#freq-build-btn') | ||
456 | build_btn.click() | ||
457 | self.wait_until_visible('#latest-builds') | ||
458 | wait_until_build(self, 'queued cloning starting parsing failed') | ||
459 | lastest_builds = self.driver.find_elements( | ||
460 | By.XPATH, | ||
461 | '//div[@id="latest-builds"]/div' | ||
462 | ) | ||
463 | self.assertTrue(len(lastest_builds) >= 2) | ||
464 | last_build = lastest_builds[0] | ||
465 | try: | ||
466 | cancel_button = last_build.find_element( | ||
467 | By.XPATH, | ||
468 | '//span[@class="cancel-build-btn pull-right alert-link"]', | ||
469 | ) | ||
470 | cancel_button.click() | ||
471 | except NoSuchElementException: | ||
472 | # Skip if the build is already cancelled | ||
473 | pass | ||
474 | wait_until_build_cancelled(self) | ||
475 | |||
476 | # Create a new project for remaining asserts | ||
477 | project_id = self.create_new_project(self.PROJECT_NAME + "-MostBuilt", '2', None, True) | ||
478 | url = reverse('project', args=(project_id,)) | ||
479 | self.get(url) | ||
480 | self.wait_until_visible('#config-nav') | ||
481 | |||
482 | current_url = self.driver.current_url | ||
483 | url = current_url.split('?')[0] | ||
484 | |||
485 | # Create a new builds | ||
486 | self._create_builds() | ||
487 | |||
488 | # back to project page | ||
489 | self.driver.get(url) | ||
490 | |||
491 | self.wait_until_visible('#project-page') | ||
492 | |||
493 | # Most built recipes | ||
494 | most_built_recipes = self.driver.find_element( | ||
495 | By.XPATH, '//*[@id="project-page"]/div[1]/div[3]') | ||
496 | title = most_built_recipes.find_element(By.TAG_NAME, 'h3') | ||
497 | self.assertIn("Most built recipes", title.text) | ||
498 | # check can select a recipe and build it | ||
499 | self.wait_until_visible('#freq-build-list') | ||
500 | recipe_list = self.find('#freq-build-list') | ||
501 | recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li') | ||
502 | self.assertTrue( | ||
503 | len(recipe_list_items) > 0, | ||
504 | msg="No recipes found in the most built recipes list", | ||
505 | ) | ||
506 | rebuild_from_most_build_recipes(recipe_list_items) | ||
507 | |||
diff --git a/bitbake/lib/toaster/tests/functional/utils.py b/bitbake/lib/toaster/tests/functional/utils.py index 7269fa1805..72345aef9f 100644 --- a/bitbake/lib/toaster/tests/functional/utils.py +++ b/bitbake/lib/toaster/tests/functional/utils.py | |||
@@ -8,7 +8,7 @@ | |||
8 | 8 | ||
9 | 9 | ||
10 | from time import sleep | 10 | from time import sleep |
11 | from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, TimeoutException | 11 | from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, TimeoutException, WebDriverException |
12 | from selenium.webdriver.common.by import By | 12 | from selenium.webdriver.common.by import By |
13 | 13 | ||
14 | from orm.models import Build | 14 | from orm.models import Build |
@@ -36,7 +36,7 @@ def wait_until_build(test_instance, state): | |||
36 | if 'failed' in str(build_state).lower(): | 36 | if 'failed' in str(build_state).lower(): |
37 | break | 37 | break |
38 | except NoSuchElementException: | 38 | except NoSuchElementException: |
39 | continue | 39 | pass |
40 | except TimeoutException: | 40 | except TimeoutException: |
41 | break | 41 | break |
42 | start_time += 1 | 42 | start_time += 1 |
@@ -48,7 +48,6 @@ def wait_until_build_cancelled(test_instance): | |||
48 | """ | 48 | """ |
49 | timeout = 30 | 49 | timeout = 30 |
50 | start_time = 0 | 50 | start_time = 0 |
51 | build = None | ||
52 | while True: | 51 | while True: |
53 | try: | 52 | try: |
54 | if start_time > timeout: | 53 | if start_time > timeout: |
@@ -64,19 +63,17 @@ def wait_until_build_cancelled(test_instance): | |||
64 | if 'failed' in str(build_state).lower(): | 63 | if 'failed' in str(build_state).lower(): |
65 | break | 64 | break |
66 | if 'cancelling' in str(build_state).lower(): | 65 | if 'cancelling' in str(build_state).lower(): |
67 | # Change build state to cancelled | 66 | pass |
68 | if not build: # get build object only once | ||
69 | build = Build.objects.last() | ||
70 | build.outcome = Build.CANCELLED | ||
71 | build.save() | ||
72 | if 'cancelled' in str(build_state).lower(): | 67 | if 'cancelled' in str(build_state).lower(): |
73 | break | 68 | break |
74 | except NoSuchElementException: | ||
75 | continue | ||
76 | except StaleElementReferenceException: | ||
77 | continue | ||
78 | except TimeoutException: | 69 | except TimeoutException: |
79 | break | 70 | break |
71 | except NoSuchElementException: | ||
72 | pass | ||
73 | except StaleElementReferenceException: | ||
74 | pass | ||
75 | except WebDriverException: | ||
76 | pass | ||
80 | start_time += 1 | 77 | start_time += 1 |
81 | sleep(1) # take a breath and try again | 78 | sleep(1) # take a breath and try again |
82 | 79 | ||
diff --git a/bitbake/lib/toaster/tests/toaster-tests-requirements.txt b/bitbake/lib/toaster/tests/toaster-tests-requirements.txt index 71cc083436..6243c00a36 100644 --- a/bitbake/lib/toaster/tests/toaster-tests-requirements.txt +++ b/bitbake/lib/toaster/tests/toaster-tests-requirements.txt | |||
@@ -5,3 +5,5 @@ pytest-env==1.1.0 | |||
5 | pytest-html==4.0.2 | 5 | pytest-html==4.0.2 |
6 | pytest-metadata==3.0.0 | 6 | pytest-metadata==3.0.0 |
7 | pytest-order==1.1.0 | 7 | pytest-order==1.1.0 |
8 | requests | ||
9 | |||
diff --git a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py index bd398f0012..aee9bbcd14 100644 --- a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py +++ b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py | |||
@@ -233,7 +233,6 @@ def filter_sizeovertotal(package_object, total_size): | |||
233 | 233 | ||
234 | return '{:.1%}'.format(float(size)/float(total_size)) | 234 | return '{:.1%}'.format(float(size)/float(total_size)) |
235 | 235 | ||
236 | from django.utils.safestring import mark_safe | ||
237 | @register.filter | 236 | @register.filter |
238 | def format_vpackage_rowclass(size): | 237 | def format_vpackage_rowclass(size): |
239 | if size == -1: | 238 | if size == -1: |
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index 40aed265dc..061e6436c8 100644 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -372,7 +372,6 @@ def _get_parameters_values(request, default_count, default_order): | |||
372 | # set cookies for parameters. this is usefull in case parameters are set | 372 | # set cookies for parameters. this is usefull in case parameters are set |
373 | # manually from the GET values of the link | 373 | # manually from the GET values of the link |
374 | def _set_parameters_values(pagesize, orderby, request): | 374 | def _set_parameters_values(pagesize, orderby, request): |
375 | from django.urls import resolve | ||
376 | current_url = resolve(request.path_info).url_name | 375 | current_url = resolve(request.path_info).url_name |
377 | request.session['%s_count' % current_url] = pagesize | 376 | request.session['%s_count' % current_url] = pagesize |
378 | request.session['%s_orderby' % current_url] =orderby | 377 | request.session['%s_orderby' % current_url] =orderby |
@@ -699,7 +698,6 @@ class LazyEncoder(json.JSONEncoder): | |||
699 | return super(LazyEncoder, self).default(obj) | 698 | return super(LazyEncoder, self).default(obj) |
700 | 699 | ||
701 | from toastergui.templatetags.projecttags import filtered_filesizeformat | 700 | from toastergui.templatetags.projecttags import filtered_filesizeformat |
702 | import os | ||
703 | def _get_dir_entries(build_id, target_id, start): | 701 | def _get_dir_entries(build_id, target_id, start): |
704 | node_str = { | 702 | node_str = { |
705 | Target_File.ITYPE_REGULAR : '-', | 703 | Target_File.ITYPE_REGULAR : '-', |
diff --git a/bitbake/lib/toaster/toastermain/settings.py b/bitbake/lib/toaster/toastermain/settings.py index e06adc5a93..d2a449627f 100644 --- a/bitbake/lib/toaster/toastermain/settings.py +++ b/bitbake/lib/toaster/toastermain/settings.py | |||
@@ -298,7 +298,6 @@ SOUTH_TESTS_MIGRATE = False | |||
298 | 298 | ||
299 | # We automatically detect and install applications here if | 299 | # We automatically detect and install applications here if |
300 | # they have a 'models.py' or 'views.py' file | 300 | # they have a 'models.py' or 'views.py' file |
301 | import os | ||
302 | currentdir = os.path.dirname(__file__) | 301 | currentdir = os.path.dirname(__file__) |
303 | for t in os.walk(os.path.dirname(currentdir)): | 302 | for t in os.walk(os.path.dirname(currentdir)): |
304 | modulename = os.path.basename(t[0]) | 303 | modulename = os.path.basename(t[0]) |