-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathComponentFactory.kt
150 lines (133 loc) · 5.93 KB
/
ComponentFactory.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package ru.romanow.playwright
import com.microsoft.playwright.Locator
import com.microsoft.playwright.Page
import com.microsoft.playwright.options.AriaRole
import ru.romanow.playwright.annotations.Component
import ru.romanow.playwright.annotations.FindBy
import ru.romanow.playwright.annotations.Parent
import java.lang.reflect.Field
import kotlin.reflect.KClass
import kotlin.reflect.jvm.javaType
class ComponentFactory private constructor() {
companion object {
fun <T : BaseComponent> create(page: Page, cls: KClass<T>): T {
val constructor =
cls.constructors.first { it.parameters.size == 1 && it.parameters[0].type.javaType == Page::class.java }
val element = constructor.call(page)
fillLocators(page, cls, element)
fillComponents(page, cls, element)
return element
}
private fun <T : BaseComponent> fillComponents(page: Page, cls: KClass<T>, element: T) {
val fields: List<Field> = cls.java.declaredFields.filter { it.isAnnotationPresent(Component::class.java) }
for (field in fields) {
val component = field.getAnnotation(Component::class.java)
val internalElement = create(page, component.type)
field.isAccessible = true
field[element] = internalElement
}
}
private fun <T : BaseComponent> fillLocators(page: Page, cls: KClass<T>, element: T) {
val fields: List<Field> = cls.java.declaredFields.filter { it.isAnnotationPresent(FindBy::class.java) }
for (field in fields) {
val findBy = field.getAnnotation(FindBy::class.java)
val locator = calculateLocator(page, findBy)
field.isAccessible = true
field[element] = locator
}
}
private fun calculateLocator(page: Page, findBy: FindBy): Locator {
return if (parentExists(findBy.parent)) {
val parent: Locator = calculateLocator(page, buildSelector(findBy.parent))
calculateLocator(parent, buildSelector(findBy))
} else {
calculateLocator(page, buildSelector(findBy))
}
}
private fun calculateLocator(page: Page, selector: Selector): Locator {
if (selector.byTestId != null) {
// by [data-testid='<name>']
return page.getByTestId(selector.byTestId)
} else if (selector.byCss != null) {
// by css locator
return page.locator("css=" + selector.byCss)
} else if (selector.byXpath != null) {
// by xpath locator
return page.locator("xpath=" + selector.byXpath)
} else if (selector.byRole != null) {
// by role and (optionally) accessible name
val options: Page.GetByRoleOptions? =
if (selector.byText != null) Page.GetByRoleOptions().setName(selector.byText) else null
return page.getByRole(selector.byRole, options)
} else if (selector.byText != null) {
// by text
return page.getByText(selector.byText)
}
throw IllegalArgumentException(
"Each @FindBy must have at least one selector (byTestId, byCss, byXPath, byText or byRole"
)
}
private fun calculateLocator(parent: Locator, selector: Selector): Locator {
if (selector.byTestId != null) {
// by [data-testid='<name>']
return parent.getByTestId(selector.byTestId)
} else if (selector.byCss != null) {
// by css locator
return parent.locator("css=" + selector.byCss)
} else if (selector.byXpath != null) {
// by xpath locator
return parent.locator("xpath=" + selector.byXpath)
} else if (selector.byRole != null) {
// by role and (optionally) accessible name
val options: Locator.GetByRoleOptions? =
if (selector.byText != null) Locator.GetByRoleOptions().setName(selector.byText) else null
return parent.getByRole(selector.byRole, options)
} else if (selector.byText != null) {
// by text
return parent.getByText(selector.byText)
}
throw IllegalArgumentException(
"Each @Parent must have at least one selector (byTestId, byCss, byXPath or byRole"
)
}
private fun buildSelector(findBy: FindBy) = Selector(
byCss = findBy.byCss,
byXpath = findBy.byXpath,
byTestId = findBy.byTestId,
byText = findBy.byText,
byRole = findBy.byRole
)
private fun buildSelector(parent: Parent) = Selector(
byCss = parent.byCss,
byXpath = parent.byXpath,
byTestId = parent.byTestId,
byRole = parent.byRole
)
private fun parentExists(selector: Parent): Boolean {
return selector.byCss.isNotEmpty() ||
selector.byXpath.isNotEmpty() ||
selector.byTestId.isNotEmpty() ||
selector.byRole !== AriaRole.NONE
}
}
}
internal class Selector(
byTestId: String? = null,
byCss: String? = null,
byXpath: String? = null,
byText: String? = null,
byRole: AriaRole = AriaRole.NONE
) {
val byTestId: String?
val byCss: String?
val byXpath: String?
val byText: String?
val byRole: AriaRole?
init {
this.byTestId = if (!byTestId.isNullOrEmpty()) byTestId else null
this.byCss = if (!byCss.isNullOrEmpty()) byCss else null
this.byXpath = if (!byXpath.isNullOrEmpty()) byXpath else null
this.byText = if (!byText.isNullOrEmpty()) byText else null
this.byRole = if (byRole != AriaRole.NONE) byRole else null
}
}