diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9de1083 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +*.log +*test.rb +*.gem +Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..031dbf9 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'rspec', require: 'spec' diff --git a/README.md b/README.md index f55a6ef..5589f99 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ -# apress-selenium_eti \ No newline at end of file +# apress-selenium_eti + +--- +Межпроектные автотесты ЕТИ и мини-ЕТИ. diff --git a/apress-selenium_eti.gemspec b/apress-selenium_eti.gemspec new file mode 100644 index 0000000..e24fc9d --- /dev/null +++ b/apress-selenium_eti.gemspec @@ -0,0 +1,23 @@ +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'apress/selenium_eti/version' + +Gem::Specification.new do |spec| + spec.name = 'apress-selenium_eti' + spec.version = Apress::SeleniumEti::VERSION + spec.authors = ['Fyodor Parmanchukov'] + spec.email = ['rezerbit@gmail.ru'] + + spec.summary = 'Selenium eti gem' + spec.description = 'Cross-project eti and mini-eti autotests' + spec.homepage = 'https://github.com/abak-press/abak-selenium-eti' + + spec.files = `git ls-files -z`.split("\x0") + spec.require_paths = ['lib'] + + spec.add_runtime_dependency 'page-object', '~> 2.0.0' # В 2.1 выпилена поддержка Selenium + spec.add_runtime_dependency 'activesupport' + spec.add_runtime_dependency 'rspec' + spec.add_runtime_dependency 'selenium-webdriver' + spec.add_runtime_dependency 'pry-byebug' +end diff --git a/lib/apress/selenium_eti.rb b/lib/apress/selenium_eti.rb new file mode 100644 index 0000000..e96e6ee --- /dev/null +++ b/lib/apress/selenium_eti.rb @@ -0,0 +1,2 @@ +gem_directory = Gem::Specification.find_by_name("apress-selenium_eti").gem_dir +Dir["#{gem_directory}/lib/pages/**/*.rb"].each { |file| require file } diff --git a/lib/apress/selenium_eti/spec/company_site/minieti_spec.rb b/lib/apress/selenium_eti/spec/company_site/minieti_spec.rb new file mode 100644 index 0000000..ace7674 --- /dev/null +++ b/lib/apress/selenium_eti/spec/company_site/minieti_spec.rb @@ -0,0 +1,190 @@ +require 'spec_helper' + +describe 'Мини-ЕТИ' do + cs_mini_eti_page = CompanySite::MiniEtiPage.new + cs_main_page = CompanySite::MainPage.new + + before(:all) do + log_in_as(:user) + navigate_to_company_catalog + cs_main_page.close_banner + end + + describe 'Поля' do + before(:all) { cs_mini_eti_page.add_product } + + context 'когда заполняем имя' do + before(:all) do + @name = Faker::Number.number(5) + cs_mini_eti_page.name = @name + end + + it 'введенное имя отображается' do + expect(cs_mini_eti_page.product_name).to include @name + end + + context 'когда добавляем картинку' do + before(:all) do + @thermometer_value = cs_mini_eti_page.thermometer_value + cs_mini_eti_page.load_image(IMAGE_PATH) + end + + it 'картинка появляется' do + expect(cs_mini_eti_page.image_loaded?).to be true + end + + it 'увеличивается градус на термометре' do + cs_mini_eti_page.wait_until { cs_mini_eti_page.save_status == 'Все изменения сохранены' } + expect(cs_mini_eti_page.thermometer_value).to be @thermometer_value + CONFIG['battery_percents']['image'] + end + + after(:all) do + cs_mini_eti_page.close_image_uploader + end + end + end + + context 'когда заполняем цену' do + before(:all) do + @thermometer_value = cs_mini_eti_page.thermometer_value + @price = Faker::Number.number(3) + cs_mini_eti_page.price = @price + end + + it 'введенная цена отображается' do + expect(cs_mini_eti_page.price_value).to include @price + end + + it 'увеличивается градус на термометре' do + cs_mini_eti_page.wait_until { cs_mini_eti_page.save_status == 'Все изменения сохранены' } + expect(cs_mini_eti_page.thermometer_value.to_i).to be @thermometer_value + CONFIG['battery_percents']['price'] + end + end + + context 'когда заполняем цену от и до' do + before(:all) do + cs_mini_eti_page.add_product + @thermometer_value = cs_mini_eti_page.thermometer_value + @price_from = Faker::Number.number(2) + @price_to = Faker::Number.number(3) + cs_mini_eti_page.set_price_from_to(@price_from, @price_to) + end + + it 'введенная цена отображается' do + expect(cs_mini_eti_page.price_value).to include @price_from + expect(cs_mini_eti_page.price_value).to include @price_to + end + end + + context 'когда заполняем цену со скидкой' do + before(:all) do + cs_mini_eti_page.add_product + @thermometer_value = cs_mini_eti_page.thermometer_value + @price = Faker::Number.number(3) + @discount_price = Faker::Number.number(2) + cs_mini_eti_page.set_discount_price(@price, @discount_price) + end + + it 'введенные цены и дата окончания скидки отображаются' do + expect(cs_mini_eti_page.discount_price_value).to include @discount_price + expect(cs_mini_eti_page.previous_price_value).to include @price + expect(cs_mini_eti_page.discount_expires_at_date_value).to include Time.now.strftime("%d.%m.%Y") + end + end + + context 'когда заполняем наличие' do + before { cs_mini_eti_page.exists = true } + + it 'введенная цена отображается' do + expect(cs_mini_eti_page.exists_value).to include 'в наличии' + end + end + + context 'когда заполняем рубрику' do + before(:all) { cs_mini_eti_page.set_rubric(CONFIG['mini_eti']['rubric']) } + + it 'привязывается рубрика' do + expect(cs_mini_eti_page.rubric_cell).to include CONFIG['mini_eti']['rubric'] + end + + context 'когда отменяем действие' do + before(:all) { cs_mini_eti_page.operation_undo } + + it 'рубрика исчезает' do + expect(cs_mini_eti_page.rubric_cell).to include 'Указать рубрику' + end + + context 'когда повторяем отмененное действие' do + before(:all) { cs_mini_eti_page.operation_redo } + + it 'привязывается рубрика' do + expect(cs_mini_eti_page.rubric_cell).to include CONFIG['mini_eti']['rubric'] + end + end + end + end + end + + describe 'Пагинатор' do + context 'когда переходим на вторую страницу' do + before(:all) { cs_mini_eti_page.page_2 } + + it 'открывается вторая страница' do + expect(no_page_errors?).to be true + expect(cs_mini_eti_page.page_2_not_exists?).to be true + expect(cs_mini_eti_page.page_1?).to be true + end + + context 'когда возвращаемся на первую страницу' do + before(:all) { cs_mini_eti_page.page_1 } + + it 'открывается первая страница' do + expect(no_page_errors?).to be true + expect(cs_mini_eti_page.page_1_not_exists?).to be true + expect(cs_mini_eti_page.page_2?).to be true + end + end + end + end + + describe 'Удаление товара' do + before do + cs_mini_eti_page.add_product + @name = Faker::Number.number(5) + cs_mini_eti_page.name = @name + cs_mini_eti_page.delete + end + + it 'товар удаляется' do + expect(cs_mini_eti_page.product_name).not_to eq @name + end + end + + describe 'Копирование товара' do + before do + cs_mini_eti_page.add_product + + cs_mini_eti_page.name = @name = Faker::Pokemon.name + cs_mini_eti_page.price = @price = Faker::Number.number(5) + cs_mini_eti_page.wait_until(45) { cs_mini_eti_page.save_status == 'Все изменения сохранены' } + + cs_mini_eti_page.copy_product + cs_mini_eti_page.wait_until(45) { cs_mini_eti_page.save_status == 'Все изменения сохранены' } + end + + it 'товар копируется' do + expect(cs_mini_eti_page.price_values_elements[0].text).to eq cs_mini_eti_page.price_values_elements[1].text + end + end + + context 'когда выбираем количество товаров на странице' do + before do + cs_mini_eti_page.choose_amount_of_products_on_page = '50' + cs_mini_eti_page.wait_until { cs_mini_eti_page.save_status == 'Все изменения сохранены' } + end + + it 'количество товаров на странице равно выбранному значению' do + expect((cs_mini_eti_page.product_elements.size <= 50) && (cs_mini_eti_page.product_elements.size > 20)) + end + end +end diff --git a/lib/apress/selenium_eti/version.rb b/lib/apress/selenium_eti/version.rb new file mode 100644 index 0000000..4fa68c0 --- /dev/null +++ b/lib/apress/selenium_eti/version.rb @@ -0,0 +1,5 @@ +module Apress + module SeleniumEti + VERSION = '0.0.0'.freeze + end +end diff --git a/lib/pages/company_site/mini_eti_page.rb b/lib/pages/company_site/mini_eti_page.rb new file mode 100644 index 0000000..8433d9d --- /dev/null +++ b/lib/pages/company_site/mini_eti_page.rb @@ -0,0 +1,174 @@ +# TODO: удалить неиспользуемые локаторы +module CompanySite + class MiniEtiPage < Page + checkbox(:product_checkbox, css: '.js-check-product') + checkbox(:deal_checkbox, css: '.js-input-deals') + button(:save_deals, css: 'div.ui-dialog div > button:nth-child(1)') + button(:add_products_to_deal, css: '.js-deals-config') + button(:save, xpath: "//*[contains(text(), 'Подтвердить актуальность')]") + span(:progress_bar, css: '#pb') + button(:to_catalog, css: '.ml15') + span(:product_name, css: '.js-eti-name > .pt-td-content-wrapper') + span(:save_status, css: '.js-status-bar-content') + button(:add_products_menu, css: '.sb-label') + button(:add_product_manually, css: '.js-add-product') + span(:empty_product_name, xpath: "//*[text()[contains(.,'Указать название')]]") + + span(:name_cell, xpath: "//*[@data-placeholder='Указать название']") + span(:price_cell, xpath: "//*[contains(text(), 'Указать цену')]") + span(:exist_cell, xpath: "//*[contains(text(), 'Указать наличие')]") + button(:add_product, css: '.new.js-add-product') + text_area(:edit_text_area, css: '.edit-text') + + text_area(:price_text_area, css: '.js-text-price') + text_area(:price_from, xpath: "(//*[@class = 'pv-text-field js-text-price'])[2]") + text_area(:price_to, css: '.js-text-price-max') + text_area(:previous_price, css: '.js-text-price-prev') + text_area(:discount_price, css: '.js-product-form-discount-price') + text_area(:discount_expires_at_date, css: '.js-discount-expires-at') + button(:save_price, css: '.ui-button.ui-widget.ui-state-default.ui-corner-all.ui-button-text-only') + + span(:price_value, css: '.bp-price.fsn') + spans(:price_values, css: '.bp-price.fsn') + span(:discount_price_value, css: '.discount .bp-price.fsn') + span(:previous_price_value, css: '.bp-price.fwn.fsn') + span(:discount_expires_at_date_value, css: '.discount-date') + + button(:exists_true, xpath: "//li/a[contains(text(), 'в наличии')]") + span(:exists_value, css: '.cost-dog-link') + span(:upload_image, name: 'images') + image(:image, css: '.ibb-img.js-img') + # HACK: цепляемся за .ui-resizable, потому что больше нет уникальных идентификаторов + button(:close_image_uploader, css: '.ui-resizable .ui-dialog-titlebar-close') + + span(:image_uploader, css: '.ui-dialog.ui-widget.ui-widget-content.ui-corner-all.ui-front.ui-draggable') + button(:image_cell, css: '.fa-camera') + button(:image_upload_btn, css: '.js-upload-input') + span(:thermometer, css: '.js-battery-wrapper') + span(:rubric_cell, css: '.js-rubric-preview-link') + text_area(:rubric_search, css: '.js-input-rubric-search') + button(:rubric_search_submit, css: '.js-button-rubric-search') + button(:first_rubric_search_result, css: '.src-link') + link(:page_2, xpath: "//*[@data-page='2']") + link(:page_1, xpath: "//*[@data-page='1']") + button(:delete_product, css: '.js-delete-product') + button(:copy_product, css: '.js-copy-product') + span(:found_products_count, css: '.js-products-count') + radio_button(:from_to, xpath: "(//*[@class = 'va-1 mr5 js-select-type-price'])[2]") + radio_button(:discount, xpath: "(//*[@class = 'va-1 mr5 js-select-type-price'])[3]") + + button(:operation_undo, css: 'div.operation.undo') + button(:operation_redo, css: 'div.operation.redo') + + select_list(:choose_amount_of_products_on_page, css: '.ptrfap-choose-amount') + divs(:product, css: 'tr.pt-tr') + + alias old_confirm confirm + def save + old_confirm + confirm_not_exists?(30) + end + + def delete + confirm(true) { delete_product } + end + + def set_rubric(text) + browser + .action + .move_to(rubric_cell_element.element) + .click + .perform + + self.rubric_search = text + rubric_search_submit + first_rubric_search_result + end + + def load_image(path) + wait_until { save_status == 'Все изменения сохранены' } + + browser + .action + .move_to(image_cell_element.element) + .click + .perform + + upload_file(upload_image_element, path) + wait_until { image_loaded? } + end + + def thermometer_value + thermometer.tr('%', '').to_i + end + + def name=(text) + browser + .action + .move_to(name_cell_element.element) + .click + .send_keys(Selenium::WebDriver::Keys::KEYS[:enter]) + .send_keys(text) + .send_keys(Selenium::WebDriver::Keys::KEYS[:enter]) + .perform + end + + def set_price_from_to(from, to) + browser + .action + .move_to(price_cell_element.element) + .click + .perform + + select_from_to + self.price_from = from + self.price_to = to + save_price + end + + def set_discount_price(previous_price, discount_price) + browser + .action + .move_to(price_cell_element.element) + .click + .perform + + select_discount + + self.previous_price = previous_price + self.discount_price = discount_price + discount_expires_at_date_element.element.send_keys(Selenium::WebDriver::Keys::KEYS[:enter]) + + save_price + end + + def price=(text) + browser + .action + .move_to(price_cell_element.element) + .click + .send_keys(price_text_area_element.element, text) + .perform + + try_to(:save_price) + end + + def price + price_cell_element.text + end + + def exists=(is_exist) + browser + .action + .move_to(exist_cell_element.element) + .click + .perform + + wait_until { save_status == 'Все изменения сохранены' } + + exists_true if is_exist + end + + ActiveSupport.run_load_hooks(:'apress/selenium_eti/company_site/mini_eti_page', self) + end +end