2.4.9 ListView

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

Объект данного типа является наиболее сложным объектом списков. Его особенностью является то, что данный вид списков может содержать несколько колонок, более того у него могут быть дополнительные объекты ( заголовок и кнопки заголовков колонок). Как и все списки, данный метод обладает свойствами и методами: sValue , iValue , lsContents, GetItemCount(), Select(…), GetContents(…) и т.д. То есть с этими свойствами и методами мы можем работать так же, как и в других списках. Теперь надо выявить, что нам может понадобиться еще. Имеется ввиду некая функциональность, которая нам пригодиться при разработке тесткейса. Откроем диалог List View, создадим некоторый список и посмотрим, что с ним можно сделать. Во-первых, первыми пунктами нашего следующего шага будет создание заголовка списка, где надо убедиться, что появится некоторая колонка. То есть нужен метод, который проверяет, есть ли некоторая колонка или нет. Колонка может задаваться индексом, а может и текстом, то есть нужен тип который может соответствовать типам данных STRING, INTEGER. В данном случае удобным типом является TABLECOL, который может хранить значения либо STRING либо INTEGER. Теперь надо определиться, как искать нужную колонку. Каждый объект класса ListView содержит в себе объект заголовка – Header, который в свою очередь содержит кнопки, соответствующие заголовку колонки. Таким образом нам нужно проверить на существование кнопки либо по индексу, либо по тексту. Надо переопределить класс ListView (если он еще не переопределен, конечно). В common.inc файле впишем строки вида:

Code

[+] //* Winclass: ListView : ListView
	[ ] //* Description: ListView class extension
[+] winclass ListView : ListView
	[ ] 
	[ ] //*************************************************************************************************
	[+] //* Method: BOOLEAN HasColumn( TABLECOL Col )
		[ ] //* Description: Verifies if column with name/index Col exists
		[ ] //* Arguments: TABLECOL Col - column to check existence of
		[ ] //* Return values: TRUE - such column exists, FALSE - otherwise
		[ ] //*************************************************************************************************
	[+] BOOLEAN HasColumn( TABLECOL Col )
		[+] switch( TypeOf( Col ) )
			[+] case INTEGER
				[ ] return this.GetColumnCount() >= Col
			[+] case STRING
				[ ] return this.Header("#1").PushButton(Col).bExists
			[+] default 
				[ ] return FALSE

 

В 4Test-e есть возможность создавать типы, которые позволяют хранить значения одного из доступных типов (аналогом в С является union). Для определения реального типа значения используется функция TypeOf.

 

Как указывалось выше, специфика списков данного вида является наличие множества колонок. В соответствии с этим, метод GetContents содержит параметры, первый из которых соответствует номеру колонки, из которой нужно извлечь содержимое. Но удобно было бы извлекать параметры из колонки, которая задана названием. Для этого нам нужно написать 2 метода. 1-й по заданному заголовку колонки определяет индекс, а 2-й метод извлекает список строк – содержимое колонки, заданной либо индексом, либо текстом заголовка. Для первого метода достаточно воспользоваться свойством iIndex, определенным для всех классов-наследников AnyWin, которое возвращает индекс элемента данного типа (фактически такой же индекс задается в теге, который отвечает за номер элемента по порядку, начиная с левого верхнего угла родительского окна). Во 2-м случае используются вариации вызова метода GetContents. В этом же файле дописываем следующее:

Code

[+] //* Winclass: ListView : ListView
	[ ] //* Description: ListView class extension
[+] winclass ListView : ListView
	...................................................................
	[ ] //*************************************************************************************************
	[+] //* Method: INTEGER GetColIndex( STRING sColName )
		[ ] //* Description: Returns the index of ListView column with column name given as parameter
		[ ] //* Arguments: STRING sColName - column name to find index for
		[ ] //* Return values: column index or 0 if no such column found
		[ ] //*************************************************************************************************
	[+] INTEGER GetColIndex( STRING sColName )
		[+] do
			[ ] return this.Header("#1").PushButton(sColName).iIndex
		[+] except
			[ ] return 0
	[ ] 
	[ ] //*************************************************************************************************
	[+] //* Method: LIST OF STRING GetColText( TABLECOL Col )
		[ ] //* Description: Retrieves contents of the specified column
		[ ] //* Arguments: TABLECOL Col - column name/index to retreive data from
		[ ] //* Return values: column contents
		[ ] //*************************************************************************************************
	[+] LIST OF STRING GetColText( TABLECOL Col )
		[+] if( !this.HasColumn(Col) )
			[ ] return {}
		[+] switch( TypeOf( Col ) )
			[+] case INTEGER
				[ ] return this.GetContents( Col )
			[+] case STRING
				[ ] return this.GetContents( this.GetColIndex(Col) )
			[+] default 
				[ ] return {}

 

 

Теперь допишем свойство для диалога ListView, которое работает с текстовым полем edtItemText. Откроем файл TestApp.inc и допишем следующее:

Code

	[+] window TestApp_Controls dListView
		[ ] tag "List View"
		[ ] 
		[+] TextField edtItemText
			[ ] tag "Item Text"
		...............................................................................................
		[ ] 
		[+] property sItemText
			[+] VOID Set( STRING sValue )
				[ ] this.edtItemText.sValue = sValue
			[+] STRING Get()
				[ ] return this.edtItemText.sValue
		[ ] 

 

 

Функциональность мы уже заготовили ( её чудесно можно использовать везде, где идет работы с объектами данного типа), теперь приступим к написанию шага. Перейдем к файлу TestApp.t. На первом этапе нам нужно добавить колонки, занести элементы в список и проверить, что ввод был осуществлен правильно. Делать это будем так: в цикле занесем колонки и проверим правильность добавления с помощью метода HasColumn; в двойном цикле (по строкам и по колонкам) заносим элементы списка, затем перебираем колонки, извлекаем из них содержимое (список) и уже полученный список строк проверяем поэлементно. Если именовать элементы по определенному, то проверка не составит трудностей. Реализация выглядит так:

Code

		[ ] wTestApp.Control.ListView.Pick()
		[+] with dListView
			[+] if( !.bExists )
				[ ] Error("List View dialog didn't appear")
			[ ] 
			[+] for i = 1 to 3
				[ ] .sItemText = "Col{i}"
				[ ] .btnAddColumn.Click()
				[+] if( !.lstTheListView.HasColumn("Col{i}") )
					[ ] Error("Column ""Col{i}"" wasn't added")
				[ ] 
			[ ] iValue = .lstTheListView.GetColumnCount() 
			[+] for i = 1 to 3
				[+] for j = 1 to iValue
					[ ] .sItemText = "Val{i}-{j}"
					[ ] .btnAddItem.Click()
			[ ] 
			[+] for i = 1 to iValue
				[ ] lsValue = .lstTheListView.GetColText(i)
				[+] for j = 1 to ListCount( lsValue )
					[+] if( lsValue[j] != "Val{j}-{i}" )
						[ ] Error("Item {j}:{i} is incorrect. Expected: ""Val{j}-{i}"" . Actual: ""{lsValue[j]}"" ",FALSE)

 

 

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

Code

			[+] for i = 1 to iValue
				[ ] .lstTheListView.DoubleSelect(i)
				[+] if( !dMsg.Exists(2) )
					[ ] Error("No message appears")
				[+] if( dMsg.statMsg.sValue != "Double select of item {i} detected" )
					[ ] Error("Unexpected message: ""{dMsg.statMsg.sValue}"" ")
				[ ] dMsg.btnOK.Click()
			[ ] 
			[ ] ClickEnabledAndCheck( dListView , .lstTheListView )

 

 

Обратите внимание, что появление сообщения мы ждем командой dMsg.Exists(2), вместо dMsg.bExists. Проблема в том, что окно может появиться не тут же, а через небольшой (очень небольшой) промежуток времени либо может не распознаться сразу. Поэтому здесь установлена небольшая задержка.

 

Далее мы переходим к проверке функциональности Set Data/ Clear All. Нам нужно ввести строку 3*4, нажать на Set Data и проверить, что список заполнился 3-мя колонками и 4-мя строками. Количество колонок возвращается методом GetColumnCount, а количество строк – методом GetItemCount ( принадлежность данного метода классу ListView не указана Help-e, но данный метод имеется и в данном классе). Для данного этапа нам все известно, так что пишем:

Code

			[ ] .btnClearAll.Click()
			[ ] .sItemText = "3*4"
			[ ] .btnSetData.Click()
			[ ] 
			[+] if( .lstTheListView.GetColumnCount() != 3 )
				[ ] Error("Incorrect column count. Expected: 3 Actual: {.lstTheListView.GetColumnCount()}")
			[+] if( .lstTheListView.GetItemCount() != 4 )
				[ ] Error("Incorrect row count. Expected: 4 Actual: {.lstTheListView.GetItemCount()}")

 

 

А теперь опять очистим список и проверим, что он пустой и готов для ввода новых данных:

Code

			[ ] .btnClearAll.Click()
			[+] if( .lstTheListView.GetColumnCount() )
				[ ] Error("Some columns were not removed")
			[+] if( .lstTheListView.GetItemCount() )
				[ ] Error("Some rows were not removed")

 

 

А теперь специфическая задача – создать файл с определенным содержимым, а затем ввести имя этого файля в поле Item Text и нажать Set Data. Для определенности, файл будет с фиксированным именем и находиться в той же папке, что и данный скрипт. Строка имени файла образуется так:

Code
  “{GetProgramDir()}\Test.tmp”

 

 

GetProgramDir() вернет каталог, в котором находится выполняемый в данный момент скрипт.

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

 

Для работы с файлом нам понадобится переменная HFILE hFile, которую мы добавим в блок объявления переменных, а также следующие функции:

  • SYS_FileExists – проверяет файл на существование
  • SYS_RemoveFile – удаляет файл
  • FileOpen – открывает файл. Принимает параметром имя файла и режим открытия, который в нашем случае задается константойFM_WRITE (перезапись)
  • FileWriteLine – записывает в файл строку
  • FileClose – закрывает файл

Подробно эти функции описаны в Help-e, мы на них не будем останавливаться. Итак, порядок действий такой:

  • Если файл уже существует, то мы его удаляем
  • Открываем файл для записи (если его нет, то он создастся)
  • Построчно записываем в него нужный текст
  • Закрываем файл
  • Вводим в поле Item Text строку @{GetProgramDir()}\Test.tmp ( имя файла, который мы записали ) жмем Set Data и проверяем правильность данных.

Поскольку данные вводились тоже по определенным правилам, то и проверка будет осуществлена исходя из данных правил. Соответственно код имеет вид:

Code

			[+] lsValue = 
				[ ] 3*4
				[ ] Column1
				[ ] Column2
				[ ] Column3
				[ ] Item1-1
				[ ] Item1-2
				[ ] Item1-3
				[ ] Item2-1
				[ ] Item2-2
				[ ] Item2-3
				[ ] Item3-1
				[ ] Item3-2
				[ ] Item3-3
				[ ] Item4-1
				[ ] Item4-2
				[ ] Item4-3
			[ ] 
			[+] if( SYS_FileExists("{GetProgramDir()}\Test.tmp") )
				[ ] SYS_RemoveFile("{GetProgramDir()}\Test.tmp")
			[ ] hFile = FileOpen("{GetProgramDir()}\Test.tmp",FM_WRITE)
			[+] for each sValue in lsValue
				[ ] FileWriteLine(hFile,sValue)
			[ ] FileClose(hFile)
			[ ] 
			[ ] .sItemText = "@{GetProgramDir()}\Test.tmp"
			[ ] .btnSetData.Click()
			[ ] 
			[+] for i = 1 to 3
				[+] if( !.lstTheListView.HasColumn("Column{i}") )
					[ ] Error("No column ""Column{i}"" found",FALSE)
					[ ] continue
				[ ] 
				[ ] lsValue = .lstTheListView.GetColText("Column{i}")
				[ ] 
				[+] for j = 1 to 4
					[ ] sValue = "Item{j}-{i}"
					[+] if( sValue != lsValue[j] )
						[ ] Error("Item {i}:{j} is incorrect. Expected: ""{sValue}"" . "+
          "Actual: ""{lsValue[j]}"" ",FALSE)

 

 

Наиболее часто объект ListView можно наблюдать в окне Windows Explorer-a. Все эти значки папок и файлов – это не что иное как элементы объекта ListView. И если помните, то у этого объекта есть несколько способов отображения: большие значки, маленькие значки, список, таблица. Следующим этапом мы как раз будем варьировать данные режимы отображения. Для этого мы поочередно будем выбирать элементы радио-группы View. Проверить способ отображения данного объекта можно методом GetView(), который возвращает одну из констант: LVV_ICON, LVV_SMALL_ICON, LVV_LIST, LVV_DETAILS. Реализация имеет вид:

Code

			[ ] lsValue = .rlstView.lsContents
			[+] for i = 1 to ListCount(lsValue)
				[ ] .rlstView.sValue = lsValue[i]
				[+] switch( lsValue[i] )
					[+] case "Icon"
						[ ] iValue = LVV_ICON
					[+] case "Small icon"
						[ ] iValue = LVV_SMALL_ICON
					[+] case "List"
						[ ] iValue = LVV_LIST
					[+] case "Details"
						[ ] iValue = LVV_DETAILS
				[ ] 
				[+] if( .lstTheListView.GetView() != iValue )
					[ ] Error("Incorrect view mode for List View. Expected: {lsValue[i]}. Actual: {.lstTheListView.GetView()}")

 

 

Вот так это выглядит. Обратите внимание на switch. В данном случае он работает со строками, чего не получается стандартными способами добиться скажем в языке С, С++.

 

Проставим все галочки в секции Options. Вот так:

Code

			[+] lsValue = {...}
				[ ] "Editable"
				[ ] "Header"
				[ ] "Icons"
				[ ] "MultiSelect"
			[+] for each sValue in lsValue
				[ ] dListView.@("cb{sValue}").bValue = TRUE
				[+] if( !dListView.@("cb{sValue}").bValue )
					[ ] Error("Unable to check ""{sValue}"" field")

 

 

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

Code

			[ ] .lstTheListView.MultiSelect(1)
			[ ] .lstTheListView.MultiSelect(3)
			[ ] 
			[+] if( .lstTheListView.liValue != {1,3} )
				[ ] Error("Multiselection is incorrect")

 

 

И последнее для данного шага – снять флаг с поля Header проверить, что заголовок списка исчез и выйти. На существование заголовок проверять не имеет смысла, так как реально заголовок не исчезает. Он просто становится невидимым. Для проверки элемента на видимость используется метод IsVisible. Этот метод является общим для всех видов объектов. Его зачастую имеет смысл применять для веб-приложений, когда некоторые объекты могут быть просто невидимы ( например кэшируются открытые разделы страницы и они сохраняются в окне, но визуально не отображаются ). Итак, завершается шаг следующим кодом:

Code

			[ ] .cbHeader.bValue = FALSE
			[+] if( .lstTheListView.Header("#1").IsVisible() )
				[ ] Error("List View Header still exists",FALSE)
			[ ] 
			[+] if( !.Exit() )
				[ ] Error("Unable to close List View dialog")

 


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