Estou no desenvolvimento front-end há tempo suficiente para ver uma tendência ao longo dos anos: desenvolvedores mais jovens trabalhando com um novo paradigma de programação sem entender o contexto histórico dele. É claro que é perfeitamente compreensível não saber alguma coisa. A web é um lugar muito grande, com um conjunto diversificado de habilidades e especialidades, e nem sempre sabemos o que não sabemos. A aprendizagem neste campo é uma jornada contínua, e não algo que acontece uma vez e termina. Caso em questão: alguém da minha equipe perguntou se era possível saber se os usuários navegavam para fora de uma guia específica na IU. Apontei o evento beforeunload do JavaScript. Mas aqueles que já abordaram isso antes sabem que isso é possível porque foram atingidos por alertas sobre dados não salvos em outros sites, para os quais o beforeunload é um caso de uso típico. Também indiquei os eventos pageHide e VisibilityChange ao meu colega para garantir. Como eu soube disso? Porque surgiu em outro projeto, não porque eu estudei sobre isso quando aprendi JavaScript inicialmente. O fato é que as estruturas front-end modernas estão apoiadas nos ombros dos gigantes da tecnologia que as precederam. Eles abstraem práticas de desenvolvimento, muitas vezes para uma melhor experiência do desenvolvedor que reduz, ou até mesmo elimina, a necessidade de conhecer ou tocar o que tradicionalmente são conceitos de front-end essenciais que todos provavelmente deveriam saber. Considere o Modelo de Objeto CSS (CSSOM). Você pode esperar que qualquer pessoa que trabalhe com CSS e JavaScript tenha muita experiência prática em CSSOM, mas nem sempre será esse o caso. Havia um projeto React para um site de comércio eletrônico em que trabalhei, onde precisávamos carregar uma folha de estilo para o provedor de pagamento selecionado no momento. O problema era que a folha de estilo carregava em todas as páginas quando era realmente necessária apenas em uma página específica. O desenvolvedor encarregado de fazer isso acontecer nunca carregou uma folha de estilo dinamicamente. Novamente, isso é totalmente compreensível quando o React abstrai a abordagem tradicional que você poderia ter adotado. O CSSOM provavelmente não é algo que você precisa no seu trabalho diário. Mas é provável que você precise interagir com ele em algum momento, mesmo que seja um caso único. Essas experiências me inspiraram a escrever este artigo. Existem muitos recursos e tecnologias da web existentes que você nunca tocará diretamente em seu trabalho diário. Talvez você seja relativamente novo no desenvolvimento web e simplesmente não os conheça porque está imerso na abstração de uma estrutura específica que não exige que você a conheça profundamente, ou mesmo que não a conheça. Estou falando especificamente sobre XML, que muitos de nós sabemos que é uma linguagem antiga não totalmente diferente do HTML. Estou trazendo isso à tona por causa de discussões recentes do WHATWG sugerindo que uma parte significativa da pilha XML conhecida como programação XSLT deveria ser removida dos navegadores. Este é exatamente o tipo de tecnologia mais antiga que temos há anos e que poderia ser usada para algo tão prático quanto a situação do CSSOM em que minha equipe se encontrava. Você já trabalhou com XSLT antes? Vamos ver se nos apoiamos fortemente nessa tecnologia mais antiga e aproveitamos seus recursos fora do contexto do XML para resolver os problemas do mundo real hoje. XPath: a API central A tecnologia XML mais importante e talvez a mais útil fora de uma perspectiva XML direta é o XPath, uma linguagem de consulta que permite encontrar qualquer nó ou atributo em uma árvore de marcação com um elemento raiz. Tenho uma afeição pessoal pelo XSLT, mas isso também depende do XPath, e a afeição pessoal deve ser deixada de lado na importância da classificação. O argumento para remover o XSLT não faz nenhuma menção ao XPath, então suponho que ainda seja permitido. Isso é bom porque XPath é a API central e mais importante neste conjunto de tecnologias, especialmente ao tentar encontrar algo para usar fora do uso normal de XML. É importante porque, embora os seletores CSS possam ser usados para localizar a maioria dos elementos da sua página, eles não conseguem encontrar todos eles. Além disso, os seletores CSS não podem ser usados para encontrar um elemento com base em sua posição atual no DOM. XPath pode. Agora, alguns de vocês que estão lendo isto podem conhecer XPath, e outros não. XPath é uma área de tecnologia muito grande, e não posso realmente ensinar todos os conceitos básicos e também mostrar coisas legais para fazer com isso em um único artigo como este. Na verdade, tentei escrever esse artigo, mas a publicação média da Smashing Magazine não ultrapassa 5.000 palavras. Eu já estava em mais de2.000 palavras, ainda na metade do básico. Então, vou começar a fazer coisas legais com XPath e fornecer alguns links que você pode usar para o básico se achar essas coisas interessantes. Combinando XPath e CSS XPath pode fazer muitas coisas que os seletores CSS não conseguem ao consultar elementos. Mas os seletores CSS também podem fazer algumas coisas que o XPath não pode, ou seja, consultar elementos pelo nome da classe.
CSS XPath .myClass /*[contém(@classe, "minhaClasse")]
Neste exemplo, CSS consulta elementos que contêm um nome de classe .myClass. Enquanto isso, o exemplo XPath consulta elementos que contêm uma classe de atributo com a string “myClass”. Em outras palavras, ele seleciona elementos com myClass em qualquer atributo, incluindo elementos com o nome de classe .myClass — bem como elementos com “myClass” na string, como .myClass2. XPath é mais amplo nesse sentido. Então, não. Não estou sugerindo que devemos descartar o CSS e começar a selecionar todos os elementos via XPath. Esse não é o ponto. A questão é que o XPath pode fazer coisas que o CSS não pode e ainda pode ser muito útil, mesmo sendo uma tecnologia mais antiga na pilha do navegador e pode não parecer óbvia à primeira vista. Vamos usar as duas tecnologias juntas não apenas porque podemos, mas porque aprenderemos algo sobre XPath no processo, tornando-o mais uma ferramenta em sua pilha — uma que você talvez não soubesse que estava lá o tempo todo! O problema é que o método document.evaluate do JavaScript e os vários métodos de seletor de consulta que usamos com as APIs CSS para JavaScript são incompatíveis. Criei uma API de consulta compatível para começar, embora, admito, não tenha pensado muito nisso, pois é diferente do que estamos fazendo aqui. Aqui está um exemplo funcional bastante simples de um construtor de consulta reutilizável: Veja o Pen queryXPath [bifurcado] de Bryan Rasmussen. Adicionei dois métodos ao objeto de documento: queryCSSSelectors (que é essencialmente querySelectorAll) e queryXPaths. Ambos retornam um objeto queryResults:
{ queryType: nós | corda | número | booleano, resultados: any[] // elementos html, elementos xml, strings, números, booleanos, queryCSSSelectors: (consulta: string, alteração: booleano) => queryResults, queryXpaths: (consulta: string, alteração: booleano) => queryResults }
As funções queryCSSSelectors e queryXpaths executam a consulta que você fornece sobre os elementos da matriz de resultados, desde que a matriz de resultados seja do tipo nós, é claro. Caso contrário, retornará um queryResult com um array vazio e um tipo de nós. Se a propriedade amend for definida como verdadeira, as funções alterarão seus próprios queryResults. Sob nenhuma circunstância isso deve ser usado em um ambiente de produção. Estou fazendo isso apenas para demonstrar os vários efeitos do uso das duas APIs de consulta juntas. Consultas de exemplo Quero mostrar alguns exemplos de diferentes consultas XPath que demonstram algumas das coisas poderosas que elas podem fazer e como podem ser usadas no lugar de outras abordagens. O primeiro exemplo é //li/text(). Isso consulta todos os elementos li e retorna seus nós de texto. Então, se consultarmos o seguinte HTML:
- um
- dois
- três
…isto é o que é retornado:
{"queryType":"xpathEvaluate","resultados":["um","dois","três"],"resultType":"string"}
Em outras palavras, obtemos o seguinte array: ["um","dois","três"]. Normalmente, você consultaria os elementos li para obter isso, transformaria o resultado dessa consulta em um array, mapearia o array e retornaria o nó de texto de cada elemento. Mas podemos fazer isso de forma mais concisa com XPath: document.queryXPaths("//li/text()").resultados.
Observe que a maneira de obter um nó de texto é usar text(), que se parece com uma assinatura de função — e é. Ele retorna o nó de texto de um elemento. Em nosso exemplo, existem três elementos li na marcação, cada um contendo texto ("um", "dois" e "três").
Vejamos mais um exemplo de consulta text(). Suponha que esta seja nossa marcação:
Vamos escrever uma consulta que retorne o valor do atributo href: document.queryXPaths("//a[text() = 'Entrar']/@href").resultados.
Esta é uma consulta XPath no documento atual, assim como no último exemplo, mas desta vez retornamos o atributo href de um link (um elemento) que contém o texto “Sign In”. O real retornouo resultado é ["/login.html"]. Visão geral das funções XPath Existem várias funções XPath e provavelmente você não está familiarizado com elas. Acho que há vários que vale a pena conhecer, incluindo o seguinte:
começa comSe um texto começa com outro exemplo de texto específico, começa com(@href, 'http:') retorna verdadeiro se um atributo href começa com http:. containsSe um texto contiver outro exemplo de texto específico, contains(text(), "Smashing Magazine") retornará verdadeiro se um nó de texto contiver as palavras “Smashing Magazine” em qualquer lugar. countRetorna uma contagem de quantas correspondências existem em uma consulta. Por exemplo, count(//*[starts-with(@href, 'http:']) retorna uma contagem de quantos links no nó de contexto possuem elementos com um atributo href que contém o texto começando com http:. substringFunciona como substring JavaScript, exceto que você passa a string como argumento. Por exemplo, substring("meu texto", 2, 4) retorna "y t". substring-beforeRetorna a parte de uma string antes de outra string. Por exemplo, substing-before("meu texto", " ") retorna "meu". Da mesma forma, substring-before("hi","bye") retorna uma string vazia. substring-afterRetorna a parte de uma string após outra string. Por exemplo, substing-after("meu texto", " ") retorna "texto". Da mesma forma, substring-after("hi","bye")retorna uma string vazia. normalize-spaceRetorna a string do argumento com espaços em branco normalizados removendo os espaços em branco iniciais e finais e substituindo sequências de caracteres de espaço em branco por um único espaço. notRetorna um booleano verdadeiro se o argumento for falso, caso contrário, falso. trueRetorna booleano verdadeiro. falseRetorna booleano falso. concatA mesma coisa que concat JavaScript, exceto que você não o executa como um método em uma string. Em vez disso, você insere todas as strings que deseja concatenar. comprimento da stringIsso não é o mesmo que comprimento da string JavaScript, mas retorna o comprimento da string fornecida como argumento. translateThis pega uma string e altera o segundo argumento para o terceiro argumento. Por exemplo, translate("abcdef", "abc", "XYZ") gera XYZdef.
Além dessas funções XPath específicas, há uma série de outras funções que funcionam exatamente da mesma forma que suas contrapartes em JavaScript — ou equivalentes em basicamente qualquer linguagem de programação — que você provavelmente também acharia úteis, como piso, teto, redondo, soma e assim por diante. A demonstração a seguir ilustra cada uma dessas funções: Veja as funções numéricas Pen XPath [bifurcadas] de Bryan Rasmussen. Observe que, como a maioria das funções de manipulação de strings, muitas das numéricas recebem uma única entrada. Isso ocorre, é claro, porque eles devem ser usados para consultas, como no último exemplo XPath: //li[piso(texto()) > 250]/@val
Se você usá-los, como faz a maioria dos exemplos, você acabará executando-os no primeiro nó que corresponder ao caminho. Existem também algumas funções de conversão de tipo que você provavelmente deve evitar porque o JavaScript já tem seus próprios problemas de conversão de tipo. Mas pode haver momentos em que você deseja converter uma string em um número para compará-la com algum outro número. As funções que definem o tipo de algo são booleano, número, string e nó. Estes são os tipos de dados XPath importantes. E como você pode imaginar, a maioria dessas funções pode ser usada em tipos de dados que não são nós DOM. Por exemplo, substring-after pega uma string como já abordamos, mas pode ser a string de um atributo href. Também pode ser apenas uma string:
const testSubstringAfter = document.queryXPaths("substring-after('olá mundo',' ')");
Obviamente, este exemplo nos devolverá o array de resultados como ["mundo"]. Para mostrar isso em ação, criei uma página de demonstração usando funções em coisas que não são nós DOM: Veja o Pen queryXPath [bifurcado] de Bryan Rasmussen. Você deve observar o aspecto surpreendente da função de tradução, que é que se você tiver um caractere no segundo argumento (ou seja, a lista de caracteres que deseja traduzir) e nenhum caractere correspondente para traduzir, esse caractere será removido da saída. Assim, isto:
traduzir('Olá, meu nome é Inigo Montoya, você matou meu pai, prepare-se para morrer','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','*')
…resulta na string, incluindo espaços: ["* * ** "]
Isso significa que a letra “a” está sendo traduzida para um asterisco (*), mas todos os outros caracteres que não possuem tradução dada a string de destino são completamente removidos. O espaço em branco é tudo que nos restaentre os caracteres “a” traduzidos. Então, novamente, esta consulta:
traduzir('Olá, meu nome é Inigo Montoya, você matou meu pai, prepare-se para morrer','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','*****************************************************'))
…não tem o problema e gera um resultado parecido com este:
"***** ** **** ** ***** ******* *** ****** ** ****** ******* ** ***"
Você pode perceber que não existe uma maneira fácil em JavaScript de fazer exatamente o que a função de tradução XPath faz, embora, para muitos casos de uso, replaceAll por expressões regulares possa lidar com isso. Você poderia usar a mesma abordagem que demonstrei, mas isso não é o ideal se tudo o que você deseja é traduzir as strings. A demonstração a seguir envolve a função de tradução do XPath para fornecer uma versão JavaScript: Veja a função de tradução da caneta [bifurcada] de Bryan Rasmussen. Onde você pode usar algo assim? Considere a criptografia Caesar Cipher com um deslocamento de três casas (por exemplo, criptografia top de linha de 48 a.C.):
traduzir("César está planejando cruzar o Rubicão!", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "XYZABCDEFGHIJKLMNOPQRSTUVWxyzabcdefghijklmnopqrstuvw")
O texto de entrada “César está planejando cruzar o Rubicão!” resulta em “Zxbpxo fp mixkkfkd ql zolpp qeb Oryfzlk!” Para dar outro exemplo rápido de diferentes possibilidades, criei uma função metal que recebe uma entrada de string e usa uma função de tradução para retornar o texto, incluindo todos os caracteres que recebem tremas. Veja a função Pen metal [bifurcada] de Bryan Rasmussen.
metal const = (str) => { return traduzir(str, "AOUaou","ÄÖÜäöü"); }
E, se receber o texto “Regras do Motley Crue, rock on dudes!”, retorna “Mötley Crüe rüles, röck ön dudes!” Obviamente, pode-se ter todos os tipos de usos paródicos desta função. Se for você, então este artigo da TVTropes deve fornecer muita inspiração. Usando CSS com XPath Lembre-se do nosso principal motivo para usar seletores CSS junto com XPath: CSS entende muito bem o que é uma classe, enquanto o melhor que você pode fazer com XPath são comparações de strings do atributo de classe. Isso funcionará na maioria dos casos. Mas se você se deparar com uma situação em que, digamos, alguém criou classes chamadas .primaryLinks e .primaryLinks2 e você estivesse usando XPath para obter a classe .primaryLinks, provavelmente teria problemas. Contanto que não haja nada bobo assim, você provavelmente usaria XPath. Mas lamento informar que trabalhei em lugares onde as pessoas fazem esse tipo de coisas bobas. Aqui está outra demonstração usando CSS e XPath juntos. Mostra o que acontece quando usamos o código para executar um XPath em um nó de contexto que não é o nó do documento. Veja o Pen css e o xpath juntos [bifurcados] por Bryan Rasmussen. A consulta CSS é . Relatedarticles a, que busca os dois elementos a em uma div atribuída a uma classe . Relatedarticles. Depois disso, vêm três consultas “ruins”, ou seja, consultas que não fazem o que queremos que façam quando executadas com esses elementos como nó de contexto. Posso explicar por que eles estão se comportando de maneira diferente do que você imagina. As três consultas ruins em questão são:
//text(): Retorna todo o texto do documento. //a/text(): Retorna todo o texto dentro dos links do documento. ./a/text(): não retorna resultados.
A razão para esses resultados é que, embora seu contexto seja um elemento retornado da consulta CSS, // vai contra todo o documento. Este é o ponto forte do XPath; CSS não pode ir de um nó até um ancestral e depois para um irmão desse ancestral, e descer até um descendente desse irmão. Mas o XPath pode. Enquanto isso, ./ consulta os filhos do nó atual, onde o ponto (.) representa o nó atual e a barra (/) representa ir para algum nó filho - se é um atributo, elemento ou texto é determinado pela próxima parte do caminho. Mas não há nenhum elemento filho selecionado pela consulta CSS, portanto essa consulta também não retorna nada. Existem três boas consultas na última demonstração:
.//texto(), ./texto(), normalizar-espaço(./text()).
A consulta normalize-space demonstra o uso da função XPath, mas também corrige um problema incluído nas outras consultas. O HTML está estruturado assim:
Automatizando seus testes de recursos com Selenium WebDriver
A consulta retorna um avanço de linha no início e no final do nó de texto,e normalize-space remove isso. Usar qualquer função XPath que retorne algo diferente de um booleano com uma entrada XPath se aplica a outras funções. A demonstração a seguir mostra vários exemplos: Veja os exemplos de funções Pen xpath [bifurcadas] de Bryan Rasmussen. O primeiro exemplo mostra um problema com o qual você deve estar atento. Especificamente, o seguinte código:
document.queryXPaths("substring-after(//a/@href,'https://')");
…retorna uma string:
"www.smashingmagazine.com/2018/04/feature-testing-selenium-webdriver/"
Faz sentido, certo? Essas funções não retornam arrays, mas sim strings ou números únicos. Executar a função em qualquer lugar com vários resultados retorna apenas o primeiro resultado. O segundo resultado mostra o que realmente queremos:
document.queryCSSSelectors("a").queryXPaths("substring-after(./@href,'https://')");
Que retorna uma matriz de duas strings:
["www.smashingmagazine.com/2018/04/feature-testing-selenium-webdriver/","www.smashingmagazine.com/2022/11/automated-test-results-improve-accessibility/"]
As funções XPath podem ser aninhadas como funções em JavaScript. Portanto, se conhecermos a estrutura de URL da Smashing Magazine, poderíamos fazer o seguinte (recomenda-se o uso de literais de modelo): `traduzir( substring( substring-after(./@href, ‘www.smashingmagazine.com/') ,9), '/','')`
Isso está ficando um pouco complexo a ponto de precisar de comentários que descrevam o que faz: pegue todo o URL do atributo href após www.smashingmagazine.com/, remova os primeiros nove caracteres e, em seguida, traduza o caractere de barra (/) para nada, de modo a se livrar da barra final. A matriz resultante:
["feature-testing-selenium-webdriver","resultados de testes automatizados-melhoram a acessibilidade"]
Mais casos de uso de XPath XPath pode realmente brilhar nos testes. A razão não é difícil de ver, já que o XPath pode ser usado para obter todos os elementos do DOM, de qualquer posição no DOM, enquanto o CSS não pode. Você não pode contar com que as classes CSS permaneçam consistentes em muitos sistemas de construção modernos, mas com o XPath, somos capazes de fazer correspondências mais robustas quanto ao conteúdo do texto de um elemento, independentemente de uma estrutura DOM alterada. Tem havido pesquisas sobre técnicas que permitem fazer testes XPath resilientes. Nada é pior do que ver os testes fracassarem e falharem só porque um seletor CSS não funciona mais porque algo foi renomeado ou removido. XPath também é ótimo na extração de vários localizadores. Há mais de uma maneira de usar consultas XPath para corresponder a um elemento. O mesmo acontece com CSS. Mas as consultas XPath podem detalhar as coisas de uma forma mais direcionada que limita o que é retornado, permitindo encontrar uma correspondência específica onde pode haver várias correspondências possíveis. Por exemplo, podemos usar XPath para retornar um elemento h2 específico que está contido dentro de uma div que segue imediatamente uma div irmã que, por sua vez, contém um elemento de imagem filho com um atributo data-testID="leader" nele:
não entendi esse título
Também não entendi esse título
O cabeçalho da imagem líder
Esta é a consulta: documento.queryXPaths(` //div[ seguinte-irmão::div[1] /img[@data-testID='líder'] ] /h2/ texto() `);
Vamos fazer uma demonstração para ver como tudo isso funciona: Veja a consulta Pen Complex H2 [bifurcada] de Bryan Rasmussen. Então, sim. Existem muitos caminhos possíveis para qualquer elemento em um teste usando XPath. Descontinuação do XSLT 1.0 Mencionei anteriormente que a equipe do Chrome planeja remover o suporte XSLT 1.0 do navegador. Isso é importante porque o XSLT 1.0 usa programação focada em XML para transformação de documentos que, por sua vez, depende do XPath 1.0, que é encontrado na maioria dos navegadores. Quando isso acontecer, perderemos um componente-chave do XPath. Mas dado o fato de que o XPath é realmente ótimo para escrever testes, acho improvável que o XPath como um todo desapareça tão cedo. Dito isso, percebi que as pessoas ficam interessadas em um recurso quando ele é retirado. E isso certamente é verdade no caso do XSLT 1.0 ser descontinuado. Há toda uma discussão acontecendo no Hacker News repleta de argumentos contra a suspensão de uso. A postagem em si é um ótimo exemplo de criação de uma estrutura de blog com XSLT. VocêVocê pode ler a discussão por si mesmo, mas explica como o JavaScript pode ser usado como uma correção para o XLST lidar com esse tipo de caso. Também vi sugestões de que os navegadores deveriam usar SaxonJS, que é uma porta para os mecanismos Saxon XSLT, XQUERY e XPath do JavaScript. Essa é uma ideia interessante, especialmente porque Saxon-JS implementa a versão atual dessas especificações, enquanto não existe nenhum navegador que implemente qualquer versão de XPath ou XSLT além de 1.0, e nenhum que implemente XQuery. Entrei em contato com Norm Tovey-Walsh da Saxonica, a empresa por trás do SaxonJS e outras versões do motor Saxon. Ele disse: “Se algum fornecedor de navegador estiver interessado em usar o SaxonJS como ponto de partida para a integração de tecnologias XML modernas no navegador, ficaremos entusiasmados em discutir isso com ele.” — Norm Tovey-Walsh
Mas também acrescentou: "Eu ficaria muito surpreso se alguém pensasse que pegar o SaxonJS em sua forma atual e colocá-lo na versão do navegador inalterado seria a abordagem ideal. Um fornecedor de navegador, pela natureza do fato de construir o navegador, poderia abordar a integração em um nível muito mais profundo do que podemos 'de fora'." — Norm Tovey-Walsh
É importante notar que os comentários de Tovey-Walsh vieram cerca de uma semana antes do anúncio da descontinuação do XSLT. Conclusão Eu poderia continuar indefinidamente. Mas espero que isso tenha demonstrado o poder do XPath e dado muitos exemplos que demonstram como usá-lo para alcançar grandes feitos. É um exemplo perfeito de tecnologia mais antiga na pilha de navegadores que ainda tem muita utilidade hoje, mesmo que você nunca tenha sabido que ela existia ou nunca tenha pensado em procurá-la. Leitura adicional
“Aprimorando a resiliência de testes automatizados da Web com linguagem natural” (ACM Digital Library), por Maroun Ayli, Youssef Bakouny, Nader Jalloul e Rima KilanyEste artigo fornece muitos exemplos de XPath para escrever testes resilientes. XPath (MDN)Este é um excelente ponto de partida se você quiser uma explicação técnica detalhando como o XPath funciona. Tutorial XPath (ZVON) Achei este tutorial o mais útil em meu próprio aprendizado, graças a uma riqueza de exemplos e explicações claras. XPatherThis ferramenta interativa permite trabalhar diretamente com o código.