4.3. Практика описания фрейма

Назад Содержание Дальше

От общих идей перейдем к конкретным задачам. В качестве примера возьмем страничку www.google.com, введем некоторое ключевое слово поиска, например TEST, и опишем окно результатов поиска. То есть у нас будет относительно бесформенный список ссылок, помещенных в определенные формы. Данный пример является достаточно показательным, так как он демонстрирует общие принципы описания фрейма для веб-приложений, но самое главное – данный пример покажет, как можно группировать элементы так, чтобы потом к ним можно было обращаться динамически. А динамическими элементами у нас являются ссылки. Причем, имеются также дополнительные ссылки Сохранено в кэше и Похожие страницы, которые присутствуют не во всех узлах.В общем, фрейм обещает быть интересным.

4.3.1. Описание страницы

Стартовую страницу описать не составляет труда, поэтому мы на ней сейчас останавливаться не будем, тем более, что основная работа должна развернуться вокруг списка ссылок, а там возможно понадобятся какие-то специфические настройки. Итак, введем ключевое слово TEST и начнем поиск. Высветится список ссылок. Попробуем его записать. И тут начинаются трудности. Если уровень распознавания таблиц с нулевой толщиной линий установлен в значение, отличное от максимального, то верхняя шапка окна просто не видна. Её как бы нет. Но вообще, желательно, чтоб она во фрейме присутствовала. Поэтому установим уровень распознавания таблиц с нулевой толщиной линий в максимальное значение 1,0. Опишем фрейм при таких настройках. Допустим страница имеет вид (по-крайней мере в данный момент, когда я описываю данный фрейм, страница имеет такой вид):

Фрейм изначально имеет вид:

Code

[+] window BrowserChild wGoogleResults
	[ ] tag "TEST -"
	[ ] parent Browser
	[ ] 
	[+] HtmlTable HtmlTable1 // Здесь содержится форма ввода ключевых слов и радио-кнопки поиска 
					           // ( Интернет , русскоязычные страницы , страницы из Украины )
	.....................................................................................................................................
	[ ] 
	[ ] 
	[+] HtmlTable HtmlTable2 // Пустая таблица. Её из фрейма можно будет убрать 
		[ ] tag "#2"
	.....................................................................................................................................
	[ ] 
	[ ] 
	[+] HtmlTable HtmlTable3 // Содержит статистику поиска
		[ ] tag "#3"
		[-] HtmlColumn Веб
			[ ] tag "Веб"
		[-] HtmlColumn Результаты110изпримерно
			[ ] tag "Результаты 1 - 10 из примерно 1 390 000 000 для TEST. (0,17 секунд)"

	.....................................................................................................................................
	[+] HtmlTable HtmlTable4 // Строчка с советами по поиску
		[ ] tag "#4"
	.....................................................................................................................................
	[ ] 
	[+] HtmlLink TestCentralHome1
		[ ] tag "Test Central Home"
	[+] HtmlTable TestCentralHome2
		[ ] tag "Test Central Home"
		[+] HtmlColumn ProvidesExtranetPrivacyToC
			[ ] tag "Provides extranet privacy to clients making a range of tests and surveys available to their human...?www.test.com? - 39k - Nio*"
			[+] HtmlText ProvidesExtranetPrivacyToC
				[ ] tag "Provides extranet privacy to clients making a range of tests and surveys available to their human"
			[+] HtmlText WwwTestCom39k
				[ ] tag "www.test.com? - 39k -"
			[+] HtmlText HtmlText3
				[ ] tag "-"
			[+] HtmlLink Сохранено в кэше
				[ ] tag "Сохранено в кэше"
			[+] HtmlLink Похожиестраницы
				[ ] tag "Похожие страницы"
	[ ] 
	[+] HtmlLink TestOfEnglishAsAForeignL1
		[ ] tag "Test of English as a Foreign Language (TOEFL)"
	[+] HtmlTable TestOfEnglishAsAForeignL2
		[ ] tag "Test of English as a Foreign Language (TOEFL)"
		[+] HtmlColumn InformationAboutTheTOEFLTe
			[ ] tag "Information about the TOEFL tests and services are available online. Try the TOEFL practice questions.?www.ets.org?toefl? - 23*"
			[+] HtmlText InformationAboutTheTOEFLTe
				[ ] tag "Information about the TOEFL tests and services are available online. Try the TOEFL practice questions."
			[+] HtmlText WwwEtsOrgToefl23k
				[ ] tag "www.ets.org?toefl? - 23k -"
			[+] HtmlText HtmlText3
				[ ] tag "-"
			[+] HtmlLink Сохраненовкэше
				[ ] tag "Сохранено в кэше"
			[+] HtmlLink Похожиестраницы
				[ ] tag "Похожие страницы"
	[ ] 
	.....................................................................................................................................
	[ ] 
	[+] HtmlLink ShieldsUp
		[ ] tag "Shields Up"
	[+] HtmlTable ShieldsUp1
		[ ] tag "Shields Up[1]"
		[+] HtmlColumn GRCInternetSecurityDetectio
			[ ] tag "GRC Internet Security Detection System scans on request the user's computer, especially the Windows...?https:??grc.com?x?ne.dl*"
			[+] HtmlText GRCInternetSecurityDetectio
				[ ] tag "GRC Internet Security Detection System scans on request the user's computer, especially the Windows"
			[+] HtmlText HttpsGrcComXNeDllBh0bk
				[ ] tag "https:??grc.com?x?ne.dll?bh0bkyd2 -"
			[+] HtmlLink Похожиестраницы
				[ ] tag "Похожие страницы"
	[ ] 
	[+] HtmlTable ACTIncEducationalCareer3  // Здесь содержится список страниц результатов ( ссылки на номера страниц 
					      // и ссылки на предыдущую, следующую страницы )
		[ ] tag "ACT, Inc. : Educational?Career Planning and Workforce Development[2]"
		[+] HtmlColumn No?aieoa?acoeuoaoia
			[ ] tag "No?aieoa ?acoeuoaoia:"
		[+] HtmlColumn HtmlColumn2
			[ ] tag "#2"
			[+] HtmlImage HttpWwwGoogleComUaIntl
				[ ] tag "#1"
		[+] HtmlColumn N1 // 
			[ ] tag "1"
			[+] HtmlImage HttpWwwGoogleComUaIntl
				[ ] tag "#1"
			[+] HtmlText N1
				[ ] tag "1"
		[+] HtmlColumn N2 // 
			[ ] tag "2"
			[+] HtmlLink N2
				[ ] tag "2"
			[+] HtmlImage HttpWwwGoogleComUaIntl
				[ ] tag "#1"
	.....................................................................................................................................

		[+] HtmlColumn N10
			[ ] tag "10"
			[+] HtmlLink N10
				[ ] tag "10"
			[+] HtmlImage HttpWwwGoogleComUaIntl
				[ ] tag "#1"
		[+] HtmlColumn Neaao?uay // Содержит ссылку на следующую страницу
			[ ] tag "Neaao?uay"
			[+] HtmlLink Neaao?uay
				[ ] tag "Neaao?uay"
			[+] HtmlImage HttpWwwGoogleComUaIntl
				[ ] tag "#1"
	[+] HtmlTable ACTIncEducationalCareer4  // Ссылки "Загрузите панель инструментов Google"
		[ ] tag "ACT, Inc. : Educational?Career Planning and Workforce Development[3]"
	.....................................................................................................................................
	[+] HtmlTable ShieldsUp2  // Нижняя форма для ввода ключевых слов и ссылки Поиск в найденном | Языковые инструменты | Как искать
		[ ] tag "Shields Up[2]"
	.....................................................................................................................................
	[+] HtmlTable ShieldsUp3 // Ссылки Домашняя страница Google - Рекламные программы - Всё о Google
		[ ] tag "Shields Up[3]"
	.....................................................................................................................................
	[+] HtmlText ©2006Google // Footer окна
		[ ] tag "©2006 Google"

 

4.3.2. Описание класса для работы со ссылками на найденные страницы

Как видно из листинга, фрейм еще нуждается в доработке. Более того, всвязи с максимальным уровнем распознавания таблиц с нулевой шириной границ многие элементы фрейма находятся на достаточно глубоком уровне вложенности. Но до них мы доберемся потом. А сейчас обратим особое внимание на ту часть фрейма, которая в последнем листинге выделена красным. Это как раз и есть набор наших ссылок. Как видим, ссылки на найденные страницы находятся на 1-м уровне вложенности в иерархии и никаких других ссылок на данном уровне нет. Это очень даже неплохо, так как такое расположение позволяет нам обращаться к любой ссылке динамически, например:

Code

	[ ] Print( wGoogleResults.HtmlLink("#1").GetLocation( ) )
	[ ] wGoogleResults.HtmlLink("#1").Click( )

 

В данном примере мы обращаемся к первой ссылке.

 

Следующее наблюдение: текст ссылки (Caption) совпадает с тегом таблицы, содержащей ссылки “Сохранено в кэше” и “Похожие страницы”, которые привязаны к данной найденной ссылке. То есть имеется способ добраться и до них ( причем этот способ вполне однозначный ). Таким образом ссылка на некоторую страницу, ссылки “Сохранено в кэше” и “Похожие страницы” формируют некоторый “узел”, в который эти объекты можно сгруппировать. Попробуем подкорректировать один из таких узлов так, чтобы он минимально зависел от текста ссылки ( то есть обобщим структуру ). Возьмем для рассмотрения первый узел. Он выглядит так:

Code

	.....................................................................................................................................
	[+] HtmlLink TestCentralHome1
		[ ] tag "Test Central Home"
	[+] HtmlTable TestCentralHome2
		[ ] tag "Test Central Home"
		[+] HtmlColumn ProvidesExtranetPrivacyToC
			[ ] tag "Provides extranet privacy to clients making a range of tests and surveys available to their human...?www.test.com? - 39k - Nio*"
			[+] HtmlText ProvidesExtranetPrivacyToC
				[ ] tag "Provides extranet privacy to clients making a range of tests and surveys available to their human"
			[+] HtmlText WwwTestCom39k
				[ ] tag "www.test.com? - 39k -"
			[+] HtmlText HtmlText3
				[ ] tag "-"
			[+] HtmlLink Сохраненовкэше
				[ ] tag "Сохранено в кэше"
			[+] HtmlLink Похожиестраницы
				[ ] tag "Похожие страницы"

 

Ссылка на найденную страницу в данном случае первая по порядку и тег “#1″ ей подойдет. Для сопряженной таблицы такой тег не подойдет, так как эта таблица не первая во фрейме. Можно, конечно, высчитать, сколько таблиц находится выше данной и скорректировать исходя из этого тег, но это не самый лучший вариант. Как уже указывалось выше, тег данной таблицы совпадает с Caption ссылки. А ссылку мы уже описали. Поэтому для таблицы тег можно реализовать как “{this.HtmlLink(“#1″).GetFullCaption()}”. Тэг будет определяться динамически, что нам, в принципе, и нужно. С внутренними объектами данной таблицы все проще: для колонки может быть использован тег “#1″ ( колонка всего одна ), а теги ссылкок “Сохранено в кэше” и “Похожие страницы” неизменны. Также можно удалить ненужные элементы (HtmlText). Данный узел можно скорректировать так:

Code

	[+] HtmlLink TestCentralHome1
		[ ] tag "#1"
	[+] HtmlTable TestCentralHome2
		[ ] tag "{this.HtmlLink("#1").GetFullCaption()}"
		[ ] 
		[+] HtmlColumn ProvidesExtranetPrivacyToC
			[ ] tag "#1"
			[+] HtmlLink Сохраненовкэше
				[ ] tag "Сохранено в кэше"
			[+] HtmlLink Похожиестраницы
				[ ] tag "Похожие страницы"

 

Теперь обратим внимание на то, что для работы с данным узлом нам не нужны объекты HtmlTable и HtmlColumn, так как они только увеличивают вложенность вызова ссылок “Сохранено в кэше” и “Похожие страницы”. Поэтому эти ссылки нужно подвинуть по иерархии на тот же уровень, что и главную ссылку узла. Также дадим объектам имена с латинскими буквами ( если в тегах кириллица еще не повредит, то идентификаторы все-таки лучше называть именами, состоящими из латинских букв), а также зададим им префиксы. Итак, окончательно узел преобразуется к виду:

Code

	[+] HtmlLink lnkLink
		[ ] tag "#1"
	[+] HtmlLink lnkCached
		[ ] tag "[HtmlTable]{this.HtmlLink("#1").GetFullCaption()}/[HtmlColumn]#1/Сохранено в кэше"
	[+] HtmlLink lnkRelativePages
		[ ] tag "[HtmlTable]{this.HtmlLink("#1").GetFullCaption()}/[HtmlColumn]#1/Похожие страницы"

 

Таким образом мы свели описание “узла” к описанию 3-х ссылок. Причем описание 1-го узла от остальных будет отличаться только индексом главной ссылки. Из этого следует, что мы выработали некоторый каркасс “узла”, у которого есть только один параметр, определяющий, с каким же узлом нужно будет работать. Такая структура позволяет создать некоторый объект, который будет содержать интерфейс для обращения к тому или иному элементу того или иного “узла”.

 

Создадим такой объект. Но для начала попытаемся описать, что же мы от него хотим. Итак, желательно, чтоб данный объект позволял работать как с отдельно взятым узлом ( выполнять операции перехода по ссылке, извлечение текста ссылки, переход на похожие страницы и т.д. ), так и со всеми узлами ( извлечь число узлов, извлечь ссылки для всех узлов и т.п. ). То есть данный объект должен работать со всем имеющимся списком узлов. Соответственно в окне wGoogleResults ( окно результатов поиска ) такой объект может быть представлен в единственном экземпляре. Данный объект реализуем в виде оконного класса. Декларация выглядит примерно так:

Code

[ ] winclass GoogleNodeList : AnyWin

 

Реально данный объект окна не представляет, но наследование от класса AnyWin позволит использовать целый ряд методов данного базового класса. Теперь опишем ключевые методы. Безусловно, это методы, которые возвращают сами объекты ссылок. Для начала опишем метод, который возвращает окно ссылки на найденную страницу. Данная ссылка может быть найдена по порядковому номеру либо по тексту ссылки. То есть параметром рассматриваемого метода может быть либо строка, либо число. В главе 2 при рассмотрении объектов списков, в частности при рассмотрении метода Select упоминался такой тип как LISTITEM, который позволяет хранить либо числовое либо строковое значение. Данный тип подойдет и для рассматриваемого метода. Принцип действия нужного нам метода заключается в том, чтобы вернуть this.HtmlColumn(“#{iItem}”), если параметром передано число, или вернуть this.HtmlColumn( iItem ), если параметром передана строка. Иначе остается сгенерировать исключение, так как ни один другой тип данных нам не подходит. Теперь рассмотрим параметр, если он передан в виде строки. Ссылки-то могут содержать различный текст, в котором могут оказаться специальные символы, например косая черта “/“. Кстати на той странице результатов, что показана выше, такая ссылка как раз имеется. Если текст данной ссылки поставить в тег таким, каким он есть, то нужный объект не будет найдет, так как символ “/” в тегах является разделителем уровней окон. Поэтому подобные символы надо замаскировать. Заменим их всех символом “?” . Для этого опишем вспомагательную функцию вида:

Code

[+] private STRING Mask( STRING sInput )
	[+] LIST OF STRING lsSpec = {...}
		[ ] "/"
		[ ] """"
	[ ] STRING sValue
	[ ] 
	[+] for each sValue in lsSpec
		[ ] sInput = StrTran( sInput , sValue , "?")
	[ ] 
	[ ] return sInput

 

Пока что список маскируемых символов ограничивается только символами косой черты и кавычки. По мере необходимости этот список можно пополнять. Теперь у нас всё есть, чтобы описать метод класса GoogleNodeList, который возвращает ссылку на найденную страницу. А выглядит он так:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] WINDOW GetLink( LISTITEM iItem )
		[+] switch TypeOf(iItem)
			[+] case INTEGER
				[ ] return this.HtmlLink("#{iItem}")
			[+] case STRING
				[ ] return this.HtmlLink( Mask( iItem ) )
			[+] default
				[ ] raise -1,"Incompatible parameter type"

 

Следует также отметить, что данный метод позволяет извлекать ссылки на страницы по тексту ссылки, используя специальные символы “*” и “?“. Дополнительные ссылки Сохранено в кэше и Похожие страницы ищутся исходя из текста ссылки на страницу. Больше ничего не требуется и методы реализуются в виде:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] WINDOW GetCached( INTEGER iItem )
		[ ] return this.HtmlLink("[HtmlTable]{this.GetLink(iItem).GetFullCaption()}/[HtmlColumn]#1/Сохранено в кэше")
	[ ]
	[+] WINDOW GetRelativePagesLink(INTEGER iItem )
		[ ] return this.HtmlLink("[HtmlTable]{this.GetLink(iItem).GetFullCaption()}/[HtmlColumn]#1/Похожие страницы")

 

Следующей функциональностью, которую не помешало бы реализовать, является проверка на существование тех или иных ссылок определенного узла. Естественно, что прежде чем пытаться пойти по некоторой ссылке, нужно проверить, что такая ссылка существует. С главной ссылкой проблем не возникает, так как нам достаточно извлечь объект этой ссылки и применить метод Exists. Код выглядит так:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] BOOLEAN LinkExists( LISTITEM liItem )
		[ ] return this.GetLink(liItem).Exists()

 

С остальными ссылками узла дело обстоит чуть сложнее. Все из-за того, что ссылки Сохранено в кэше и Похожие страницы используют текст главной ссылки, которой может и не оказаться, что повлечет генерацию исключения при попытке извлечь текст главной ссылки. Данное исключение надо перехватить, так как для данной функции это всего лишь результат, подтверждающий, что искомой ссылки нет. Проверки на существование ссылок Сохранено в кэше и Похожие страницы реализуются так:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] BOOLEAN LinkCachedExists( LISTITEM liItem )
		[+] do
			[ ] return this.GetCached(liItem).Exists()
		[+] except
			[ ] return FALSE
	[+] BOOLEAN LinkRelativePagesExists( LISTITEM liItem )
		[+] do
			[ ] return this.GetRelativePagesLink(liItem).Exists()
		[+] except
			[ ] return FALSE

 

При желании, можно добавить в данную группу функций опциональный параметр, определяющий время ожидания появления нужной ссылки. Этот параметр просто подставляется в метод Exists.

 

Следующим этапом нужно создать методы, позволяющие извлекать количество ссылок на найденные страницы и текст этих ссылок. Реализуем их через property таким образом:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] property iLinksCount
		[+] INTEGER Get( )
			[ ] INTEGER iValue = 1
			[ ] 
			[+] while( this.LinkExists( iValue ) )
				[ ] iValue++
			[ ] return iValue-1
	[ ]
	[+] property lsLinkNames
		[+] LIST OF STRING Get()
			[ ] LIST OF STRING lsValue = {""}
			[ ] INTEGER iValue = this.iLinksCount
			[ ] 
			[+] while( iValue )
				[ ] ListInsert( lsValue , 1 , this.GetLink(iValue--).GetFullCaption() )
			[ ] ListDelete(lsValue,ListCount(lsValue))
			[ ] return lsValue

 

И теперь осталась группа методов для выбора ссылок. Причем следует учитывать, что ссылку можно выбрать по номеру или по индексу, открыть ссылку можно в том же окне или в новом ( в контекстном меню выбрать пункт Открыть в новом окне). Эти соображения позволяют определить набор параметров. Первый параметр – величина типа LISTITEM ( для вызовов функций GetLink и др. ). Второй параметр – BOOLEAN, определяющий нужно ли открывать ссылку в новом окне. Данный параметр фактически влияет только на то, как будет произведен клик на ссылке. Если нужно открыть ссылку в том же окне, то клик будет сделан прямо на ссылке, в противном случае нужно вызвать PopupSelect метод, который сделает выбор из контекстного меню. Реализация метода имеет вид:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] VOID Select( LISTITEM liItem , BOOLEAN bNewWindow NULL optional )
		[ ] RECT rc
		[ ] WINDOW wWin = this.GetLink( liItem ) 
		[+] if( IsNull( bNewWindow ) || !bNewWindow )
			[ ] wWin.Click() // Открыть в том же окне
		[+] else
			[ ] rc = wWin.GetRect()
			[ ] this.PopupSelect(rc.xPos+rc.xSize/2,rc.yPos+rc.ySize/2,"#2") // Открыть в новом окне

 

Следует заметить, что ссылка извлекается сразу, чтобы потом не дублировать вызовы GetLink, тем более что данную функцию можно дополнить различными проверками, например на то, что извлеченная ссылка существует. Также обратим внимание на вызов метода PopupSelect. Данный метод сработает для окна, которое имеет визуальное представление. Но описываемому нами классу не соответствует никакого окна. Но данный класс теперь наследуется от класса AnyWin и экземпляр рассматриваемого класса своего тега не имеет, соответственно метод PopupSelect сработает для базового окна. Если бы данный класс не наследовался от AnyWin, то пришлось бы напрямую извлекать родительское окно.

 

Опишем аналогичные методы для выбора остальных ссылок узла. Реализуются они идентично:

Code

[+] winclass GoogleNodeList : AnyWin
	.....................................................................................................................................
	[+] VOID SelectCached( LISTITEM liItem , BOOLEAN bNewWindow NULL optional )
		[ ] RECT rc
		[ ] WINDOW wWin = this.GetCached( liItem )
		[+] if( IsNull( bNewWindow ) || !bNewWindow )
			[ ] wWin.Click()
		[+] else
			[ ] rc = wWin.GetRect()
			[ ] this.PopupSelect(rc.xPos + rc.xSize/2,rc.yPos + rc.ySize/2,"#2")
	[+] VOID SelectRelative( LISTITEM liItem , BOOLEAN bNewWindow NULL optional )
		[ ] RECT rc
		[ ] WINDOW wWin = this.GetRelativePagesLink(liItem)
		[+] if( IsNull( bNewWindow ) || !bNewWindow )
			[ ] wWin.Click()
		[+] else
			[ ] rc = wWin.GetRect()
			[ ] this.PopupSelect(rc.xPos+rc.xSize/2,rc.yPos+rc.ySize/2,"#2")

 

Как видно, последние 3 метода отличаются только строкой инициализации переменной wWin. Естественно, можно эти функции сгруппировать в одну и добавить еще один параметр – код ссылки, позволяющий определить на какую из ссылок узла нужно кликнуть. Это уже кому как удобней. Я предпочту оставить эти методы в таком виде, так как модификации и корректировки для этих методов могут различаться.

 

Таким образом мы описали класс для работы со списком ссылок на найденные страницы. Это одна из самых сложных частей, поэтому мы реализовали ее в первую очередь.

4.3.3. Вынесение элементов фрейма вверх по иерархии

Теперь приведем в порядок остальные части фрейма. Первым делом мы удалим из фрейма объекты, которые мы вряд ли будем использовать. В нашем случае это

Code

[+] window BrowserChild wGoogleResults
	[ ] tag "TEST -"
	[ ] parent Browser
	[ ] 
	.....................................................................................................................................
	[ ] 
	[+] HtmlTable HtmlTable2 
		[ ] tag "#2"

 

Выделенный красным фрагмент не содержит в себе объекты, с которыми можно что-то сделать. Это обычная таблица с одной колонкой и больше ничего. Уберем эту таблицу из фрейма.

 

Также скорее всего HtmlTable HtmlTable4 не понадобится, так как в этой таблице содержится какая-то вспомагательная информация. Если по тесткейсам ссылки в этой панели используются, то тогда эту таблицу можно оставить. Я эту таблицу удалю, так как я с ней работать не собираюсь, да и она не всегда появляется.

 

Следующим этапом будет формирование более-менее удобной иерархии объектов во фрейме. То есть нужные объекты мы вынесем повыше в иерархии. С объектами, находящимися выше списка ссылок на найденные страницы, проблем не возникает, эти объекты описываются однозначно. Проблема возникает с теми секциями, которые находятся ниже списка ссылок. Все эти секции помещены в таблицы. У этих таблиц тег может варьироваться. Это зависит от текста ссылок ( варьируется тег по Caption ) и от количества ссылок (варьируется тег по индексу). Поэтому теги секций, следующих за списком ссылок, придется привязывать к списку ссылок. Наиболее оптимальным вариантом будет привязка по индексу, а именно: извлекаем индекс последней таблицы списка ссылок на найденные страницы и прибавляем к нему смещение. Для примера рассмотрим секцию, содержащую ссылки Загрузить сейчас и О панели инструментов. Элементы данной секции помещены внутри таблицы, индекс которой на 2 больше, чем индекс последней таблицы списка ссылок ( между ними еще есть секция выбора страниц результатов ). Соответственно, чтобы определить тег элементов данной секции, нужно получить индекс последней таблицы списка ссылок и прибавить к этой величине 2. Для упрощения получения этих величин добавим в класс GoogleNodeList метод:

Code

[+] winclass GoogleNodeList : AnyWin
	.......................................................................................................................................
	[+] INTEGER GetLastTableIndex()
		[ ] return this.HtmlLink("[HtmlTable]{Mask(this.GetLink(this.iLinksCount).GetFullCaption())}[1]").iIndex

 

Для упрощения обращения к данному методу в пределах окна wGoogleResults допишем свойство iBaseIndex:

Code

[+] window BrowserChild wGoogleResults
	.......................................................................................................................................
	[+] property iBaseIndex
		[+] INTEGER Get()
			[ ] return this.Nodes.GetLastTableIndex()

 

Теперь таблица, содержащая ссылки Загрузить сейчас и О панели инструментов может быть описана так:

Code

[+] window BrowserChild wGoogleResults
	.......................................................................................................................................
	[+] HtmlTable Table
		[ ] tag "#{iBaseIndex+2}"

 

Это было показано описание таблицы, но нам для фрейма нужно описать ссылки, находящиеся внутри данной таблицы. Эти ссылки вынесем во фрейме на самый верхний уровень, после чего ссылки описываются в виде:

Code

[+] window BrowserChild wGoogleResults
	.......................................................................................................................................
	[+] HtmlLink lnkDownloadNow
		[ ] tag "[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/Загрузить сейчас"
	[+] HtmlLink lnkAboutToolbar
		[ ] tag "[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/О панели инструментов"
	.......................................................................................................................................

 

По аналогии можно описать и другие секции, но тут появляется еще одна проблема – вышерассмотренная секция появляется только на первой странице поиска. Если мы переключимся на другую страницу, то данная секция там не будет отображаться и соответственно теги сместятся, в частности теги таблиц. Для этих целей удобно завести функцию, которая возвращает один из возможных вариантов тегов окна. В одном из файлов, содержащих общую функциональность, поместим функцию вида:

Code

[+] STRING OneOfTheTags( WINDOW wWindow, LIST OF STRING lsTags )
	[ ] STRING sValue
	[ ] 
	[+] for each sValue in lsTags
		[+] if( wWindow.AnyWin(sValue).Exists() )
			[ ] return sValue
	[ ] return NULL

 

Параметр wWindow принимает родительское окно для элемента, для которого нужно подобрать тег. Параметр lsTags содержит список тегов, из которых нужно выбрать подходящий. Используя новую функцию, можно описать элементы секции поиска внизу страницы ( ссылки Поиск в найденном , Языковые инструменты , Как искать, а также текстовое поле для ввода и кнопка поиска ). У них смещение может варьироваться между значениями 2 и 3. Соответственно эта секция описывается в виде:

Code

[+] window BrowserChild wGoogleResults
	.......................................................................................................................................
	[+] HtmlLink lnkSearchInFound
		[ ] tag OneOfTheTags(WindowParent(this),{"[HtmlTable]#{iBaseIndex+3}/[HtmlColumn]#1/[HtmlLink]Iiene a iaeaaiiii","[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/[HtmlLink]Iiene a iaeaaiiii"})
	[+] HtmlLink lnkLangInstr
		[ ] tag OneOfTheTags(WindowParent(this),{"[HtmlTable]#{iBaseIndex+3}/[HtmlColumn]#1/?cueiaua eino?oiaiou","[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/?cueiaua eino?oiaiou"})
	[+] HtmlLink lnkHowToSearch
		[ ] tag OneOfTheTags(WindowParent(this),{"[HtmlTable]#{iBaseIndex+3}/[HtmlColumn]#1/Eae eneaou","[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/Eae eneaou"})
	[+] HtmlTextField edtSearch2
		[ ] tag OneOfTheTags(WindowParent(this),{"[HtmlTable]#{iBaseIndex+3}/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/#1","[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/#1"})
	[+] HtmlPushButton btnSearch2
		[ ] tag OneOfTheTags(WindowParent(this),{"[HtmlTable]#{iBaseIndex+3}/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/#1","[HtmlTable]#{iBaseIndex+2}/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/#1"})

 

Аналогично придется описывать секцию, содержащую ссылки Домашняя страница Google , Рекламные программы , Всё о Google, где смещение варьируется между 3 и 4. Для веб-приложений такое варьирование тегов не редкость. Страница может содержать кучу промежуточных элементов, которые не всегда, может, и нужны для автоматизации ( например рекламные баннеры ), но фрейм из-за них серьезно сдвигается. Поэтому такая вариация здесь вполне уместна.

 

4.3.4. Таблица ссылок на страницы результатов

Единственной секцией, которую мы еще не рассмотрели, является секция ссылок на страницы результатов. Фактически это таблица, содержащая ссылки на предыдущую, следующую страницы, а также ссылки на страницы по номерам. Данную секцию удобно реализовать отдельным оконным классом, чем мы сейчас и займемся. Данный класс формируется на основе таблицы, поэтому данный класс объявим в виде:

Code

[ ] winclass GooglePageNumbers : HtmlTable

 

Рассмотрим детально структуру данной таблицы. Первая колонка не содержит вообще функциональных элементов. Вторая колонка содержит ссылку на предыдущую страницу. Если открыта первая страница и, соответственно, ссылки на предыдущую страницу нет, то вторая колонка будет пустой, то есть 2-я колонка резервирует место для ссылки на предыдущую страницу. Следующие колонки содержат ссылки на номера страниц, на которые можно в данный момент переключиться. Последняя колонка содержит ссылку на следущую страницу. Таким образом у нас есть 2 фиксированные ссылки, которые мы можем сразу отразить во фрейме таким образом:

Code

[+] winclass GooglePageNumbers : HtmlTable
	[+] HtmlLink lnkPrevious
		[ ] tag "[HtmlColumn]#2/#1"
	[+] HtmlLink lnkNext
		[ ] tag "[HtmlColumn]#{this.GetColumnCount()}/#1"

 

Все остальное будет реализовано через функциональность. Например, для данного класса полезно извлекать список имен ссылок на страницы, доступные в данный момент. Это осуществляется перебором колонок, начиная с 3-й и до предпоследней. В этом интервале и содержатся ссылки. Нужно пройтись по всем колонкам в данном интервале и извлечь текст ссылок, которые находятся в этих колонках (если ссылки есть). Функциональность, реализующая данную операцию полностью укладывается в формат property, поэтому реализуем её так:

Code

[+] winclass GooglePageNumbers : HtmlTable
	..................................................................................................................
	[ ] 
	[+] property lsLinks
		[+] LIST OF STRING Get()
			[ ] LIST OF STRING lsValue
			[ ] INTEGER i
			[ ] 
			[ ] INTEGER iCount = this.GetColumnCount()
			[ ] 
			[+] for i = 3 to iCount - 1
				[+] if(this.HtmlColumn("#{i}").HtmlLink("#1").Exists())
					[ ] ListAppend( lsValue , this.HtmlColumn("#{i}").HtmlLink("#1").GetFullCaption() )
			[ ] return lsValue

 

А теперь проведем небольшой подсчет. Сколько раз данный фрагмент обратится к this? Один раз мы к нему обращаемся при извлечении количества колонок. Предположим, что в секции номеров страниц 10 номеров. Соответственно, обращений к this будет 19 ( одна из колонок содержит обычный текст вместо ссылки – это текущая страница). Итого 20 обращений. Это было бы не принципиально, если бы не одно обстоятельство. Данная секция следует сразу же за списком ссылок, поэтому тег у данной таблицы варьируется и имеет вид “[HtmlTable]#{iBaseIndex+1}”. Соответственно, при вызове свойства lsLinks 20 раз будет пересчитываться количество ссылок на странице. Это уже сейчас занимает много времени, но если предположить, что мы сделаем настройки , при которых будет на странице отображено 100 найденных ссылок, то время выполнения свойства lsLinks будет уже непозволительно большим. Чтобы как-то оптимизировать данное свойство, нужно хотя бы уменьшить количество обращений к this. Для этого нужно воспользоваться какими-то другими объектами. Например, мы можем извлечь индекс данной таблицы, а также извлечь её родительское окно и уже через него обращаться к элементам таблицы. Это потребует 2 обращения к this вместо 20, что уже ускоряет работу. Перепишем данное свойство так:

Code

[+] winclass GooglePageNumbers : HtmlTable
	..................................................................................................................
	[ ] 
	[+] property lsLinks
		[+] LIST OF STRING Get()
			[ ] LIST OF STRING lsValue
			[ ] INTEGER i
			[ ] 
			[ ] STRING sTag = "#{this.iIndex}"
			[ ] WINDOW wWin = this.GetParent()
			[ ] INTEGER iCount = wWin.HtmlTable(sTag).GetColumnCount()
			[ ] 
			[+] for i = 3 to iCount - 1
				[+] if(wWin.HtmlTable(sTag).HtmlColumn("#{i}").HtmlLink("#1").Exists())
					[ ] ListAppend( lsValue , wWin.HtmlTable(sTag).HtmlColumn("#{i}").HtmlLink("#1").GetFullCaption() )
			[ ] return lsValue

 

Таким образом мы избавились от обращения к this внутри цикла, что делает затраты времени на выполнение операции зависящими только от количества ссылок. Подобная ситуация часто будет возникать там, где объекты определяются динамически, как и в нашем случае. Поэтому код желательно оптимизировать, иначе будет неоправданная потеря времени при выполнении скриптов.

 

По аналогии с предыдущим свойством реализуем свойство, возвращающее номер текущей страницы поиска.

Code

[+] winclass GooglePageNumbers : HtmlTable
	..................................................................................................................
	[+] property iCurPage
	[+] INTEGER Get()
		[ ] INTEGER i
		[ ] 
		[ ] STRING sTag = "#{this.iIndex}"
		[ ] WINDOW wWin = this.GetParent()
		[ ] INTEGER iCount = this.GetColumnCount()
		[ ] 
		[+] for i = 3 to iCount - 1
			[+] if(wWin.HtmlTable(sTag).HtmlColumn("#{i}").HtmlText("#1").Exists())
				[ ] return Val(wWin.HtmlTable(sTag).HtmlColumn("#{i}").HtmlText("#1").GetFullCaption())
		[ ] 
		[ ] return 0

 

И осталось теперь реализовать функцию выбора нужной страницы. Выбрать можно 2-мя способами:

  • Указать номер колонки таблицы по порядку – это быстрый способ, удобен в тех случаях, когда в свое время список страниц уже был извлечен или нужно постоянно выбирать самую первую/последнюю страницу из доступных в данный момент.
  • Указать имя страницы, какое указано в тексте ссылки, на которую нужно кликнуть. Фактически передаем имя ссылки на страницу результатов

В данной функции тоже надо минимизировать обращение к this. Код реализуется в виде:

Code

[+] winclass GooglePageNumbers : HtmlTable
	..................................................................................................................
	[+] BOOLEAN Select( LISTITEM sPage )
		[ ] INTEGER iPage
		[ ] 
		[ ] STRING sTag = "#{this.iIndex}"
		[ ] WINDOW wWin = this.GetParent()
		[ ] 
		[+] switch( TypeOf( sPage ) )
			[+] case INTEGER
				[ ] iPage = sPage + 2
			[+] case STRING
				[ ] iPage = ListFind( wWin.GooglePageNumbers(sTag).lsLinks , sPage ) + 2
		[ ] 
		[+] if( iPage <= 2 )
			[ ] Print("* * * Page ""{sPage}"" wasn't found")
			[ ] return FALSE
		[ ] 
		[+] do
			[ ] wWin.HtmlTable(sTag).HtmlColumn("#{iPage}").HtmlLink("#1").Click()
		[+] except
			[ ] ExceptPrint()
			[ ] return FALSE
		[ ] 
		[ ] return TRUE

 

Все описано, теперь можно сводить. Но не забываем того, что это веб-приложение и его страницы делаются по некоторому шаблону, который в SilkTest-е соответствует оконному классу. Давайте, пройдемся по различным группам ссылок. Вверху страницы результатов поиска есть ссылки Картинки, Группы, Каталог, Дополнительно. Если пойти по первым трем ссылкам, то откроются страницы, у которых есть общая со страницой ссылок на найденные страницы составляющая – форма ввода критерия поиска в самом верху страницы. Эта часть может быть вынесена в оконный класс, который имеет вид:

Code

[+] winclass GoogleResultsBase : BrowserChild
	[ ] 
	[+] HtmlRadioList rlstSearch
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#2/[HtmlColumn]#1/Eneaou:"
	[+] HtmlTextField edtSearch
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#2/[HtmlColumn]#1/Aiiieieoaeuii »"
	[+] HtmlPushButton btnSearch
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#2/[HtmlColumn]#1/Iiene"
	[ ] 
	[+] HtmlLink lnkExtSearch
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#2/[HtmlColumn]#2/?anoe?aiiue iiene"
	[+] HtmlLink lnkSettings
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#2/[HtmlColumn]#2/Iano?ieee"
	[ ] 
	[+] HtmlLink lnkWeb
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/Aaa"
	[+] HtmlLink lnkPictures
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/Ea?oeiee"
	[+] HtmlLink lnkGroups
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/A?oiiu"
	[+] HtmlLink lnkCatalog
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/Eaoaeia"
	[+] HtmlLink lnkMore
		[ ] tag "[HtmlTable]#1/[HtmlColumn]#3/[HtmlTable]#1/[HtmlColumn]#1/[HtmlTable]#1/[HtmlColumn]#1/Aiiieieoaeuii »"

 

Соответственно, окно результатов поиска объявляется в виде:

Code

	[+] window GoogleResultsBase wGoogleResults
		[ ] parent Browser
		[ ] tag "$http:??www.google.com.ua?search?svnum=*hl=*"
		.......................................................

 

Обратите внимание на тег данного окна. Данное окно распознается по Window ID. Это достаточно уникальный идентификатор и в веб-приложениях он генерируется либо по адресу страницы, либо по атрибуту name элемента управления. Так или иначе, в веб приложениях такой тег является и уникальным и удобным для понимания, поэтому здесь мы используем его.

 

Теперь мы можем описать страницы, открываемые по ссылкам Картинки, Группы, Каталог, но мы рассмотрение этих окон пропустим, так как их написание также займет много времени и ничего нового все равно освещено не будет.

 

Единственное, что осталось перед тем как реализовать некоторый сценарий, – это описать стартовую страницу. Содержимое данного окна не меняется, поэтому фрейм не представляет сложности и описывается в виде:

Code

[+] window BrowserChild wGoogleStart
	[ ] tag "Google"
	[ ] parent Browser
	[ ] 
	[+] HtmlLink lnkPictures
		[ ] tag "[HtmlForm]#1/[HtmlTable]#1/[HtmlColumn]#1/Ea?oeiee"
	[+] HtmlLink lnkGroups
		[ ] tag "[HtmlForm]#1/[HtmlTable]#1/[HtmlColumn]#1/A?oiiu"
	[+] HtmlLink lnkCatalog
		[ ] tag "[HtmlForm]#1/[HtmlTable]#1/[HtmlColumn]#1/Eaoaeia"
	[+] HtmlLink lnkMore
		[ ] tag "[HtmlForm]#1/[HtmlTable]#1/[HtmlColumn]#1/Aiiieieoaeuii »"
	[+] HtmlRadioList rlstSearch
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#1/Eneaou:"
	[ ] 
	[+] HtmlTextField edtSearch
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#2/#1"
	[+] HtmlPushButton btnGoogleSearch
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#2/Iiene a Google"
	[+] HtmlPushButton btnLucky
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#2/Iia iiaac?o!"
	[ ] 
	[+] HtmlLink lnkExtSearch
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#3/?anoe?aiiue iiene"
	[+] HtmlLink lnkSettings
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#3/Iano?ieee"
	[+] HtmlLink lnkLangInstr
		[ ] tag "[HtmlForm]#1/[HtmlTable]#2/[HtmlColumn]#3/?cueiaua eino?oiaiou"
	[ ] 
	[+] HtmlLink lnkAdvPrograms
		[ ] tag "?aeeaiiua i?ia?aiiu"
	[+] HtmlLink lnkAllAboutGoogle
		[ ] tag "An? i Google"
	[+] HtmlLink lnkGoogleComInEnglish
		[ ] tag "Google.com in English"
	[+] HtmlLink lnkMakeHomePage
		[ ] tag "Naaeaeoa Google noa?oiaie no?aieoae!"

 

Все, основа фрейма написана. Данные окна и оконные классы поместим в файл Google.inc, а для реализации скрипта создадим файл Google.t. Можно переходить к реализации тестовых сценариев.


Назад Содержание Дальше