artespy 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.13
artespy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: artespy
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Author-email: Leonardo Hossokawa de Oliveira <estagiario.seguros04@artesp.sp.gov.br>
6
+ Requires-Python: >=3.13
7
+ Requires-Dist: lxml>=6.0.2
8
+ Requires-Dist: pandas>=2.3.3
9
+ Requires-Dist: selenium>=4.38.0
10
+ Requires-Dist: webdriver-manager>=4.0.2
File without changes
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "artespy"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Leonardo Hossokawa de Oliveira", email = "estagiario.seguros04@artesp.sp.gov.br" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = [
11
+ "lxml>=6.0.2",
12
+ "pandas>=2.3.3",
13
+ "selenium>=4.38.0",
14
+ "webdriver-manager>=4.0.2",
15
+ ]
16
+
17
+ [project.scripts]
18
+ artespy = "artespy:main"
19
+
20
+ [build-system]
21
+ requires = ["hatchling"]
22
+ build-backend = "hatchling.build"
@@ -0,0 +1,3 @@
1
+ from artespy.selenium.handler import SeleniumHandler
2
+
3
+ __all__ = ["SeleniumHandler"]
@@ -0,0 +1,22 @@
1
+ class HandlerException(Exception):
2
+ """Classe base para exceptions relacionadas ao handler."""
3
+
4
+
5
+ class UnknownAlertException(HandlerException):
6
+ pass
7
+
8
+
9
+ class ElementNotFoundException(HandlerException):
10
+ pass
11
+
12
+
13
+ class InvalidPageException(HandlerException):
14
+ pass
15
+
16
+
17
+ class SeiException(Exception):
18
+ """Classe base para exceptions relacionadas ao SEI."""
19
+
20
+
21
+ class InvalidCredentialsError(SeiException):
22
+ pass
@@ -0,0 +1,262 @@
1
+ import datetime as dt
2
+ import os
3
+ from typing import Literal
4
+
5
+ import pandas as pd
6
+ from selenium.webdriver import Chrome, ChromeOptions, ChromeService
7
+ from selenium.webdriver.support.ui import WebDriverWait
8
+ from webdriver_manager.chrome import ChromeDriverManager
9
+
10
+ from artespy.errors import HandlerException, InvalidPageException
11
+ from artespy.selenium.pages.home import HomePage
12
+ from artespy.selenium.pages.login import LoginPage
13
+ from artespy.selenium.pages.processo import ProcessoPage
14
+
15
+
16
+ class SeleniumHandler:
17
+ """Handler Selenium para automatizar operações no portal SEI."""
18
+
19
+ def __init__(self, headless: bool = False, timeout: int = 60) -> None:
20
+ self.headless: bool = headless
21
+ self.timeout: int = timeout
22
+
23
+ self.driver = None
24
+ self.wait = None
25
+ self._page = None
26
+
27
+ @property
28
+ def page(self):
29
+ if not self._page:
30
+ raise HandlerException("Página atual não inicializada.")
31
+ return self._page
32
+
33
+ def _inicializar_driver(self) -> None:
34
+ """Instanciar um novo driver e espera explícita."""
35
+ opts = ChromeOptions()
36
+ opts.add_argument("--log-level=3")
37
+ opts.add_argument("--start-maximized")
38
+ if self.headless:
39
+ opts.add_argument("--headless")
40
+ opts.add_experimental_option("excludeSwitches", ["enable-automation"])
41
+ opts.add_experimental_option("useAutomationExtension", False)
42
+ opts.add_experimental_option(
43
+ "prefs",
44
+ {
45
+ "credentials_enable_service": False,
46
+ "profile.password_manager_enabled": False,
47
+ },
48
+ )
49
+
50
+ chrome_install = ChromeDriverManager().install()
51
+ folder = os.path.dirname(chrome_install)
52
+ driver_path = os.path.join(folder, "chromedriver.exe")
53
+ svc = ChromeService(driver_path)
54
+
55
+ self.driver = Chrome(service=svc, options=opts)
56
+ self.wait = WebDriverWait(self.driver, self.timeout)
57
+
58
+ def _encerrar_driver(self) -> None:
59
+ if self.driver:
60
+ self.driver.close()
61
+ self.driver = None
62
+ if self.wait:
63
+ self.wait = None
64
+
65
+ def __enter__(self) -> "SeleniumHandler":
66
+ """Ponto de entrada do gerenciador de contexto."""
67
+ self._inicializar_driver()
68
+ return self
69
+
70
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
71
+ """Ponto de saída do gerenciador de contexto."""
72
+ self._encerrar_driver()
73
+
74
+ def _get_driver_e_espera(
75
+ self,
76
+ ) -> tuple[Chrome, WebDriverWait[Chrome]]:
77
+ if not self.driver or not self.wait:
78
+ raise HandlerException("Driver e/ou espera não inicializados.")
79
+ return self.driver, self.wait
80
+
81
+ def acessar_sei(self, usuario: str, senha: str, orgao: str = "ARTESP"):
82
+ """Acessa e faz login no portal SEI."""
83
+ driver, wait = self._get_driver_e_espera()
84
+
85
+ if not isinstance(usuario, str):
86
+ msg = "Acessando SEI: usuario deve ser uma string"
87
+ raise TypeError(msg) # pyright:ignore[reportUnreachable]
88
+ if not usuario:
89
+ msg = "Acessando SEI: usuário deve ser uma string não vazia"
90
+ raise ValueError(msg)
91
+ if not isinstance(senha, str):
92
+ msg = "Acessando SEI: senha deve ser uma string"
93
+ raise TypeError(msg) # pyright:ignore[reportUnreachable]
94
+ if not senha:
95
+ msg = "Acessando SEI: senha deve ser uma string não vazia"
96
+ raise ValueError(msg)
97
+ if not isinstance(orgao, str):
98
+ msg = "Acessando SEI: orgao deve ser uma string"
99
+ raise TypeError(msg) # pyright:ignore[reportUnreachable]
100
+ if not orgao:
101
+ msg = "Acessando SEI: órgão deve ser uma string não vazia"
102
+ raise ValueError(msg)
103
+
104
+ LoginPage(driver, wait).fazer_login(usuario, senha, orgao)
105
+ self._page = HomePage(driver, wait)
106
+ self._page.fechar_modal_novidades()
107
+
108
+ def trocar_unidade(self, unidade: str):
109
+ driver, wait = self._get_driver_e_espera()
110
+
111
+ if not isinstance(unidade, str):
112
+ msg = "Trocando unidade: unidade deve ser uma string"
113
+ raise TypeError(msg) # pyright:ignore[reportUnreachable]
114
+ if not unidade:
115
+ msg = "Trocando unidade: unidade deve ser uma string não vazia"
116
+ raise ValueError(msg)
117
+
118
+ self.page.navbar.trocar_unidade(unidade)
119
+ self._page = HomePage(driver, wait)
120
+
121
+ def buscar_processo(self, processo: str) -> bool:
122
+ driver, wait = self._get_driver_e_espera()
123
+
124
+ encontrado = self.page.navbar.buscar_processo(processo)
125
+ if encontrado:
126
+ self._page = ProcessoPage(driver, wait)
127
+ return encontrado
128
+
129
+ def ler_processos_recebidos(self) -> pd.DataFrame:
130
+ if not isinstance(self.page, HomePage):
131
+ msg = "página atual não é a página de controle de processos"
132
+ raise InvalidPageException(msg)
133
+
134
+ r = self.page.ler_tabela_recebidos()
135
+ if not r.empty:
136
+ r = (
137
+ r.drop(columns=["Unnamed: 0", "Recebidos", "Recebidos.2"])
138
+ .rename(columns={"Recebidos.1": "Processo"})
139
+ .drop_duplicates(ignore_index=True)
140
+ )
141
+
142
+ return r
143
+
144
+ def ler_processos_gerados(self) -> pd.DataFrame:
145
+ if not isinstance(self.page, HomePage):
146
+ msg = "página atual não é a página de controle de processos"
147
+ raise InvalidPageException(msg)
148
+
149
+ g = self.page.ler_tabela_gerados()
150
+ if not g.empty:
151
+ g = (
152
+ g.drop(columns=["Unnamed: 0", "Gerados", "Gerados.2"])
153
+ .rename(columns={"Gerados.1": "Processo"})
154
+ .drop_duplicates(ignore_index=True)
155
+ )
156
+
157
+ return g
158
+
159
+ def ler_processos_unidade(self) -> pd.DataFrame:
160
+ """Retorna os processos recebidos e gerados em um DF."""
161
+ r = self.ler_processos_recebidos()
162
+ g = self.ler_processos_gerados()
163
+ return pd.concat([r, g], ignore_index=True)
164
+
165
+ def ler_status(self) -> str | list[str]:
166
+ if not isinstance(self.page, ProcessoPage):
167
+ msg = "página atual não é a página de processo"
168
+ raise InvalidPageException(msg)
169
+ return self.page.ler_status()
170
+
171
+ def ler_marcadores(self) -> list[str]:
172
+ if not isinstance(self.page, ProcessoPage):
173
+ msg = "página atual não é a página de processo"
174
+ raise InvalidPageException(msg)
175
+ return self.page.ler_marcadores()
176
+
177
+ def ler_prazo(self) -> dt.date | None:
178
+ if not isinstance(self.page, ProcessoPage):
179
+ msg = "página atual não é a página de processo"
180
+ raise InvalidPageException(msg)
181
+ return self.page.ler_prazo()
182
+
183
+ def ler_andamentos(self) -> pd.DataFrame:
184
+ if not isinstance(self.page, ProcessoPage):
185
+ msg = "página atual não é a página de processo"
186
+ raise InvalidPageException(msg)
187
+ return self.page.ler_andamentos()
188
+
189
+ def mover_documentos(self, nome: str, destino: str, motivo: str) -> None:
190
+ if not isinstance(self.page, ProcessoPage):
191
+ msg = "página atual não é a página de processo"
192
+ raise InvalidPageException(msg)
193
+ if not isinstance(nome, str):
194
+ msg = "nome deve ser uma string"
195
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
196
+ if not nome:
197
+ msg = "nome deve ser uma string não vazia"
198
+ raise ValueError(msg)
199
+ if not isinstance(destino, str):
200
+ msg = "destino deve ser uma string"
201
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
202
+ if not destino:
203
+ msg = "destino deve ser uma string não vazia"
204
+ raise ValueError(msg)
205
+ if not isinstance(motivo, str):
206
+ msg = "motivo deve ser uma string"
207
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
208
+ if not motivo:
209
+ msg = "motivo deve ser uma string não vazia"
210
+ raise ValueError(msg)
211
+
212
+ self.page.mover_documentos(nome, destino, motivo)
213
+
214
+ def concluir_processo(self) -> None:
215
+ if not isinstance(self.page, ProcessoPage):
216
+ msg = "página atual não é a página de processo"
217
+ raise InvalidPageException(msg)
218
+ self.page.concluir_processo()
219
+
220
+ def anexar_processo(self, processo: str) -> None:
221
+ if not isinstance(self.page, ProcessoPage):
222
+ msg = "página atual não é a página de processo"
223
+ raise InvalidPageException(msg)
224
+ self.page.anexar_processo(processo)
225
+
226
+ def conceder_vistas(
227
+ self,
228
+ email: str,
229
+ tipo: Literal["acompanhamento", "disponibilizacao"],
230
+ motivo: str,
231
+ dias: int,
232
+ token: str,
233
+ ) -> None:
234
+ if not isinstance(self.page, ProcessoPage):
235
+ msg = "página atual não é a página de processo"
236
+ raise InvalidPageException(msg)
237
+ if not isinstance(email, str):
238
+ msg = "email deve ser uma string"
239
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
240
+ if not email:
241
+ msg = "email deve ser uma string não vazia"
242
+ raise ValueError(msg)
243
+ if not isinstance(motivo, str):
244
+ msg = "motivo deve ser uma string"
245
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
246
+ if not motivo:
247
+ msg = "motivo deve ser uma string não vazia"
248
+ raise ValueError(msg)
249
+ if not isinstance(dias, int):
250
+ msg = "dias deve ser um int"
251
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
252
+ if not dias:
253
+ msg = "dias deve ser maior que zero"
254
+ raise ValueError(msg)
255
+ if not isinstance(token, str):
256
+ msg = "token deve ser uma string"
257
+ raise TypeError(msg) # pyright: ignore[reportUnreachable]
258
+ if not token:
259
+ msg = "token deve ser uma string não vazia"
260
+ raise ValueError(msg)
261
+
262
+ self.page.conceder_vistas(email, tipo, motivo, str(dias), token)
@@ -0,0 +1,96 @@
1
+ from typing import final
2
+
3
+ from selenium.webdriver.common.by import By
4
+
5
+
6
+ @final
7
+ class Locators:
8
+ # Login
9
+ InputUsuario = By.ID, "txtUsuario"
10
+ InputSenha = By.ID, "pwdSenha"
11
+ SelOrgao = By.ID, "selOrgao"
12
+ BtnLogin = By.ID, "sbmAcessar"
13
+
14
+ # Geral
15
+ FecharModalNovidade = By.XPATH, "//*[@title='Fechar janela (ESC)']"
16
+ CxUnidade = By.CSS_SELECTOR, "#divInfraBarraSistemaPadrao #lnkInfraUnidade"
17
+ LinhaUnidade = By.XPATH, "//td[contains(text(), '{}')]"
18
+ BuscaRapida = By.ID, "txtPesquisaRapida"
19
+ ProcessoNaoEncontrado = By.CSS_SELECTOR, "#conteudo > div"
20
+
21
+ # Controle de processos
22
+ TblRecebidos = By.ID, "tblProcessosRecebidos"
23
+ SelPagRecebidos = By.ID, "selRecebidosPaginacaoSuperior"
24
+ ProxPagRecebidos = By.ID, "lnkRecebidosProximaPaginaSuperior"
25
+ TblGerados = By.ID, "tblProcessosGerados"
26
+ SelPagGerados = By.ID, "selGeradosPaginacaoSuperior"
27
+ ProxPagGerados = By.ID, "lnkGeradosProximaPaginaSuperior"
28
+
29
+ # Processo
30
+ IframeArvore = By.ID, "ifrArvore"
31
+ IframeConteudo = By.ID, "ifrConteudoVisualizacao"
32
+ IframeVisualizacao = By.ID, "ifrVisualizacao"
33
+ DivStatus = By.ID, "divArvoreInformacao"
34
+ UnidadesStatus = By.TAG_NAME, "a"
35
+ Marcadores = By.XPATH, "//*[contains(@title, 'Marcador')]"
36
+ Prazo = By.XPATH, '//*[contains(@title, "Controle de Prazo:")]'
37
+ BtnAndamentos = By.CSS_SELECTOR, "#divConsultarAndamento > a"
38
+ TipoHistorico = By.ID, "ancTipoHistorico"
39
+ TblAndamentos = By.ID, "tblHistorico"
40
+ SelPagAndamentos = By.ID, "selInfraPaginacaoSuperior"
41
+ ProxPagAndamentos = By.ID, "lnkInfraProximaPaginaSuperior"
42
+ DivArvore = By.ID, "divArvore"
43
+ Documentos = By.XPATH, "//*/a[@target='ifrConteudoVisualizacao']"
44
+ Documento = By.LINK_TEXT, "{}"
45
+ DivArvoreInfo = By.ID, "divArvoreInformacao"
46
+ # Ações
47
+ AcaoMover = By.XPATH, "//*[@title='Mover Documento para outro Processo']"
48
+ AcaoConcluir = By.XPATH, "//*[@title='Concluir Processo']"
49
+ AcaoAnexar = By.XPATH, "//*[@title='Anexar Processo']"
50
+ AcaoVistas = (
51
+ By.XPATH,
52
+ "//*[@title='Gerenciar Disponibilizações de Acesso Externo']",
53
+ )
54
+ AcaoMarcadores = By.XPATH, "//*[@title='Gerenciar Marcador']"
55
+ # Mover Documento
56
+ ProcessoDestino = By.ID, "txtProcedimentoDestino"
57
+ BtnPesquisar = By.ID, "btnPesquisar"
58
+ InputMotivoMover = By.ID, "txaMotivo"
59
+ BtnMover = By.ID, "sbmMover"
60
+ # Concluir Processo
61
+ BtnSalvarConclusao = By.ID, "sbmSalvar"
62
+ # Anexar Processo
63
+ InputAnexar = By.ID, "txtProtocolo"
64
+ BtnPesquisar = By.ID, "btnPesquisar"
65
+ BtnAnexar = By.ID, "sbmAnexar"
66
+ # Vistas
67
+ InputEmailUnidade = By.ID, "selEmailUnidade"
68
+ FiltroUsuarioExterno = By.ID, "lblUsuariosExternos"
69
+ InputDestinatario = By.ID, "txtDestinatario"
70
+ AutocompleteDestinatario = By.ID, "divInfraAjaxtxtDestinatario"
71
+ InputEmailDestinatario = By.ID, "txtEmailDestinatario"
72
+ InputMotivoVistas = By.ID, "txaMotivo"
73
+ OptVistasIntegral = By.ID, "lblIntegral"
74
+ OptVistasParcial = By.ID, "lblParcial"
75
+ PermitirInclusaoDocs = By.ID, "lblSinInclusao"
76
+ SelecionarDocs = By.ID, "imgLupaSeries"
77
+ SelecionarProtocolos = By.ID, "imgLupaProtocolos"
78
+ IframeModal = By.NAME, "modal-frame"
79
+ FecharModalDocs = By.XPATH, "//*[@title='Fechar janela (ESC)']"
80
+ SelPagDocs = By.ID, "selInfraPaginacaoSuperior"
81
+ BtnTransportar = (
82
+ By.CSS_SELECTOR,
83
+ "#divInfraBarraComandosSuperior > #btnTransportarSelecao",
84
+ )
85
+ OptDocAnexo = (
86
+ By.CSS_SELECTOR,
87
+ "a#lnkInfraID-1917 ~ div.infraCheckboxDiv > label",
88
+ )
89
+ OptDocManifestacaoTecnica = (
90
+ By.CSS_SELECTOR,
91
+ "a#lnkInfraID-64 ~ div.infraCheckboxDiv > label",
92
+ )
93
+ OptTodosDocs = By.ID, "imgInfraCheck"
94
+ InputDiasVistas = By.ID, "txtDias"
95
+ InputTokenVistas = By.ID, "pwdSenha"
96
+ BtnDisponibilizar = By.ID, "btnDisponibilizar"
@@ -0,0 +1,34 @@
1
+ from selenium.common.exceptions import TimeoutException
2
+ from selenium.webdriver import Chrome
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.ui import WebDriverWait
5
+
6
+ from artespy.selenium.pages.components.navbar import Navbar
7
+
8
+
9
+ class BasePage:
10
+ def __init__(self, driver: Chrome, wait: WebDriverWait[Chrome]) -> None:
11
+ self.driver: Chrome = driver
12
+ self.wait: WebDriverWait[Chrome] = wait
13
+
14
+ def get_texto_alerta(self, timeout=5) -> str:
15
+ try:
16
+ return (
17
+ WebDriverWait(self.driver, timeout)
18
+ .until(EC.alert_is_present())
19
+ .text
20
+ )
21
+ except TimeoutException:
22
+ return ""
23
+
24
+ def confirmar_alerta(self):
25
+ self.driver.switch_to.alert.accept()
26
+
27
+ def fechar_alerta(self):
28
+ self.driver.switch_to.alert.dismiss()
29
+
30
+
31
+ class BaseLoggedInPage(BasePage):
32
+ def __init__(self, driver: Chrome, wait: WebDriverWait[Chrome]) -> None:
33
+ super().__init__(driver, wait)
34
+ self.navbar: Navbar = Navbar(driver, wait)
@@ -0,0 +1,8 @@
1
+ from selenium.webdriver import Chrome
2
+ from selenium.webdriver.support.ui import WebDriverWait
3
+
4
+
5
+ class BaseComponent:
6
+ def __init__(self, driver: Chrome, wait: WebDriverWait[Chrome]) -> None:
7
+ self.driver: Chrome = driver
8
+ self.wait: WebDriverWait[Chrome] = wait
@@ -0,0 +1,40 @@
1
+ from selenium.common.exceptions import TimeoutException
2
+ from selenium.webdriver import Keys
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.ui import WebDriverWait
5
+
6
+ from artespy.selenium.locators import Locators as lc
7
+ from artespy.selenium.pages.components.base import BaseComponent
8
+
9
+
10
+ class Navbar(BaseComponent):
11
+ def get_unidade_atual(self) -> str:
12
+ return self.wait.until(EC.element_to_be_clickable(lc.CxUnidade)).text
13
+
14
+ def acessar_pag_unidade(self) -> None:
15
+ self.wait.until(EC.element_to_be_clickable(lc.CxUnidade)).click()
16
+
17
+ def trocar_unidade(self, unidade: str) -> None:
18
+ u = self.get_unidade_atual()
19
+ if u.lower() == unidade.strip().lower():
20
+ return
21
+
22
+ self.acessar_pag_unidade()
23
+
24
+ loc = lc.LinhaUnidade[0], lc.LinhaUnidade[1].format(unidade)
25
+ self.wait.until(EC.element_to_be_clickable(loc)).click()
26
+
27
+ def buscar_processo(self, processo: str) -> bool:
28
+ self.driver.switch_to.default_content()
29
+ busca = self.wait.until(EC.element_to_be_clickable(lc.BuscaRapida))
30
+ busca.send_keys(processo)
31
+ busca.send_keys(Keys.ENTER)
32
+
33
+ try:
34
+ _ = WebDriverWait(self.driver, 5).until(
35
+ EC.visibility_of_element_located(lc.ProcessoNaoEncontrado)
36
+ )
37
+ except TimeoutException: # processo encontrado
38
+ return True
39
+ else:
40
+ return False
@@ -0,0 +1,28 @@
1
+ import pandas as pd
2
+ from selenium.common.exceptions import TimeoutException
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.ui import WebDriverWait
5
+
6
+ from artespy.selenium.locators import Locators as lc
7
+ from artespy.selenium.pages.base import BaseLoggedInPage
8
+ from artespy.selenium.pages.mixins.table import TableMixin
9
+
10
+
11
+ class HomePage(BaseLoggedInPage, TableMixin):
12
+ def fechar_modal_novidades(self) -> None:
13
+ try:
14
+ WebDriverWait(self.driver, 5).until(
15
+ EC.element_to_be_clickable(lc.FecharModalNovidade)
16
+ ).click()
17
+ except TimeoutException:
18
+ pass # modal ausente
19
+
20
+ def ler_tabela_recebidos(self) -> pd.DataFrame:
21
+ return self.ler_tabela(
22
+ lc.TblRecebidos, lc.SelPagRecebidos, lc.ProxPagRecebidos
23
+ )
24
+
25
+ def ler_tabela_gerados(self) -> pd.DataFrame:
26
+ return self.ler_tabela(
27
+ lc.TblGerados, lc.SelPagGerados, lc.ProxPagGerados
28
+ )
@@ -0,0 +1,47 @@
1
+ from selenium.webdriver.support import expected_conditions as EC
2
+ from selenium.webdriver.support.ui import Select
3
+ from artespy.errors import InvalidCredentialsError, UnknownAlertException
4
+ from artespy.selenium.locators import Locators as lc
5
+ from artespy.selenium.pages.base import BasePage
6
+
7
+
8
+ class LoginPage(BasePage):
9
+ URL: str = "https://sei.sp.gov.br/sip/login.php?sigla_orgao_sistema=GESP&sigla_sistema=SEI&infra_url=L3NlaS8="
10
+
11
+ def acessar(self) -> None:
12
+ self.driver.get(self.URL)
13
+
14
+ def preencher_usuario(self, usuario: str) -> None:
15
+ self.wait.until(EC.element_to_be_clickable(lc.InputUsuario)).send_keys(
16
+ usuario
17
+ )
18
+
19
+ def preencher_senha(self, senha: str) -> None:
20
+ self.wait.until(EC.element_to_be_clickable(lc.InputSenha)).send_keys(
21
+ senha
22
+ )
23
+
24
+ def selecionar_orgao(self, orgao: str) -> None:
25
+ Select(
26
+ self.wait.until(EC.element_to_be_clickable(lc.SelOrgao))
27
+ ).select_by_visible_text(orgao)
28
+
29
+ def clicar_login(self) -> None:
30
+ self.wait.until(EC.element_to_be_clickable(lc.BtnLogin)).click()
31
+
32
+ def fazer_login(self, usuario: str, senha: str, orgao: str) -> None:
33
+ self.acessar()
34
+
35
+ self.preencher_usuario(usuario)
36
+ self.preencher_senha(senha)
37
+ self.selecionar_orgao(orgao)
38
+ self.clicar_login()
39
+
40
+ t = self.get_texto_alerta()
41
+ if t:
42
+ if t.lower() == "usuário ou senha inválida.":
43
+ msg = "usuário, senha ou órgão inválidos"
44
+ raise InvalidCredentialsError(msg)
45
+ else:
46
+ msg = f"alerta com mensagem inesperada: '{t}'"
47
+ raise UnknownAlertException(msg)