├── .github
└── CODEOWNERS
├── template
├── new
│ ├── system
│ │ ├── logs
│ │ │ └── README.md
│ │ └── evidences
│ │ │ ├── README.md
│ │ │ └── screen_shots
│ │ │ └── README.md
│ ├── services
│ │ └── README.md
│ ├── .bucky_home
│ └── config
│ │ ├── linkstatus_config.yml
│ │ ├── bucky_config.yml
│ │ ├── test_db_config.yml
│ │ └── e2e_config.yml
└── make_page
│ ├── sp
│ ├── parts
│ │ └── sample_page.yml
│ └── pageobject
│ │ └── sample_page.rb
│ ├── pc
│ ├── parts
│ │ └── sample_page.yml
│ └── pageobject
│ │ └── sample_page.rb
│ └── tablet
│ ├── parts
│ └── sample_page.yml
│ └── pageobject
│ └── sample_page.rb
├── example
├── hands-on
│ ├── system
│ │ ├── logs
│ │ │ └── README.md
│ │ └── evidences
│ │ │ ├── README.md
│ │ │ └── screen_shots
│ │ │ └── README.md
│ ├── services
│ │ ├── README.md
│ │ └── bucky_hands_on
│ │ │ └── pc
│ │ │ ├── parts
│ │ │ ├── github_search_list.yml
│ │ │ └── github_top.yml
│ │ │ ├── scenarios
│ │ │ ├── linkstatus
│ │ │ │ └── github_top.yml
│ │ │ └── e2e
│ │ │ │ └── search_and_assert.yml
│ │ │ └── pageobject
│ │ │ ├── github_top.rb
│ │ │ └── github_search_list.rb
│ ├── .bucky_home
│ └── config
│ │ ├── linkstatus_config.yml
│ │ ├── bucky_config.yml
│ │ ├── test_db_config.yml
│ │ └── e2e_config.yml
├── bucky-management
│ ├── system
│ │ ├── logs
│ │ │ └── README.md
│ │ └── evidences
│ │ │ ├── README.md
│ │ │ └── screen_shots
│ │ │ └── README.md
│ ├── services
│ │ ├── README.md
│ │ └── bucky_example
│ │ │ └── pc
│ │ │ ├── parts
│ │ │ ├── github_search_list.yml
│ │ │ └── github_top.yml
│ │ │ ├── scenarios
│ │ │ ├── linkstatus
│ │ │ │ └── github_top.yml
│ │ │ └── e2e
│ │ │ │ └── search_and_assert.yml
│ │ │ └── pageobject
│ │ │ ├── github_top.rb
│ │ │ └── github_search_list.rb
│ ├── .bucky_home
│ ├── config
│ │ ├── linkstatus_config.yml
│ │ ├── test_db_config.yml
│ │ ├── bucky_config.yml
│ │ └── e2e_config.yml
│ └── README.md
└── README.md
├── .rspec
├── lib
├── bucky.rb
└── bucky
│ ├── version.rb
│ ├── test_equipment
│ ├── verifications
│ │ ├── abst_verification.rb
│ │ ├── service_verifications.rb
│ │ └── e2e_verification.rb
│ ├── user_operation
│ │ ├── user_operation_logger.rb
│ │ ├── user_operator.rb
│ │ └── user_operation_helper.rb
│ ├── selenium_handler
│ │ ├── wait_handler.rb
│ │ └── webdriver_handler.rb
│ ├── test_case
│ │ ├── linkstatus_test_case.rb
│ │ ├── abst_test_case.rb
│ │ └── e2e_test_case.rb
│ ├── evidence
│ │ └── evidence_generator.rb
│ └── pageobject
│ │ ├── pages.rb
│ │ └── base_pageobject.rb
│ ├── utils
│ ├── bucky_output.rb
│ ├── yaml_load.rb
│ ├── bucky_logger.rb
│ ├── requests.rb
│ └── config.rb
│ ├── core
│ ├── test_core
│ │ ├── exit_handler.rb
│ │ ├── test_result.rb
│ │ ├── test_class_generator.rb
│ │ ├── test_case_loader.rb
│ │ └── test_manager.rb
│ ├── report
│ │ └── screen_shot_generator.rb
│ ├── database
│ │ └── db_connector.rb
│ └── exception
│ │ └── bucky_exception.rb
│ └── tools
│ └── lint.rb
├── system_testing
├── test_bucky_project
│ ├── system
│ │ ├── logs
│ │ │ └── README.md
│ │ └── evidences
│ │ │ ├── README.md
│ │ │ └── screen_shots
│ │ │ └── README.md
│ ├── services
│ │ ├── README.md
│ │ └── service_a
│ │ │ ├── sp
│ │ │ ├── parts
│ │ │ │ └── index.yml
│ │ │ ├── scenarios
│ │ │ │ ├── linkstatus
│ │ │ │ │ └── sp_link.yml
│ │ │ │ └── e2e
│ │ │ │ │ └── sp_e2e_test.yml
│ │ │ └── pageobject
│ │ │ │ └── index.rb
│ │ │ ├── tablet
│ │ │ ├── parts
│ │ │ │ └── index.yml
│ │ │ ├── pageobject
│ │ │ │ └── index.rb
│ │ │ └── scenarios
│ │ │ │ └── e2e
│ │ │ │ └── tablet_e2e_test.yml
│ │ │ └── pc
│ │ │ ├── parts
│ │ │ └── index.yml
│ │ │ ├── scenarios
│ │ │ ├── linkstatus
│ │ │ │ ├── pc_link_exclude_asterisk.yml
│ │ │ │ ├── pc_link_exclude_regex.yml
│ │ │ │ ├── pc_link_exclude_normal.yml
│ │ │ │ └── pc_link.yml
│ │ │ └── e2e
│ │ │ │ ├── setup_each_pc_e2e.yml
│ │ │ │ ├── teardown_each_pc_e2e.yml
│ │ │ │ ├── setup_teardown_each_pc_e2e.yml
│ │ │ │ └── pc_e2e.yml
│ │ │ ├── pageobject
│ │ │ └── index.rb
│ │ │ └── verifications
│ │ │ └── index.rb
│ ├── .bucky_home
│ └── config
│ │ ├── linkstatus_config.yml
│ │ ├── bucky_config.yml
│ │ ├── test_db_config.yml
│ │ └── e2e_config.yml
└── testing_code
│ ├── command.bats
│ ├── linkstatus.bats
│ └── e2e.bats
├── Gemfile
├── spec
├── utils
│ ├── config_not_in_template.yml
│ ├── bucky_config.yml
│ ├── linkstatus_config.yml
│ ├── test_db_config.yml
│ ├── bucky_output_spec.rb
│ ├── e2e_config.yml
│ ├── yaml_load_spec.rb
│ ├── config_spec.rb
│ ├── requests_spec.rb
│ └── bucky_logger_spec.rb
├── test_config
│ ├── test_config.yml
│ └── hierarchy_config
│ │ └── test_config_hierarchy.yml
├── test_code
│ └── services
│ │ └── service_a
│ │ ├── pc
│ │ ├── parts
│ │ │ └── sample_page.yml
│ │ ├── verifications
│ │ │ └── sample_page.rb
│ │ ├── scenarios
│ │ │ └── e2e
│ │ │ │ └── scenario_a.yml
│ │ └── pageobject
│ │ │ └── sample_page.rb
│ │ └── sp
│ │ └── scenarios
│ │ ├── linkstatus
│ │ └── scenario_c.yml
│ │ └── e2e
│ │ └── scenario_b.yml
├── test_equipment
│ ├── verifications
│ │ ├── abst_verification_spec.rb
│ │ └── service_verifications_spec.rb
│ ├── user_operation
│ │ ├── user_operation_logger_spec.rb
│ │ └── user_operator_spec.rb
│ ├── evidence
│ │ └── evidence_generator_spec.rb
│ ├── selenium_handler
│ │ ├── wait_handler_spec.rb
│ │ └── webdriver_handler_spec.rb
│ ├── pageobject
│ │ ├── pages_spec.rb
│ │ └── base_pageobject_spec.rb
│ └── test_case
│ │ ├── abst_test_case_spec.rb
│ │ └── e2e_test_case_spec.rb
├── version_spec.rb
├── tools
│ ├── config_spec.yml
│ ├── rule_config_spec.yml
│ └── lint_spec.rb
├── spec_helper.rb
└── core
│ ├── test_core
│ ├── test_result_spec.rb
│ ├── exit_handler_spec.rb
│ ├── test_case_loader_spec.rb
│ ├── test_class_generator_spec.rb
│ └── test_manager_spec.rb
│ ├── database
│ └── db_connector_spec.rb
│ ├── report
│ └── screen_shot_generator_spec.rb
│ └── exception
│ └── bucky_exception_spec.rb
├── bin
├── setup
└── console
├── Rakefile
├── .circleci
├── setup_rubygems.sh
├── deploy_to_rubygems.sh
└── config.yml
├── docker
└── nginx
│ ├── public
│ ├── test_page.html
│ └── index.html
│ ├── Dockerfile
│ └── nginx.conf
├── .dockerignore
├── docker-compose.dev.yml
├── Dockerfile
├── .gitignore
├── docker-compose.dev-with-bm.yml
├── docker-compose.system-test.yml
├── Dockerfile.dev
├── Dockerfile.system-test
├── .codeclimate.yml
├── .rubocop_todo.yml
├── .rubocop.yml
├── bucky-core.gemspec
├── Gemfile.lock
└── exe
└── bucky
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @lifull-dev/admin-bucky
--------------------------------------------------------------------------------
/template/new/system/logs/README.md:
--------------------------------------------------------------------------------
1 | for save logs directory.
2 |
--------------------------------------------------------------------------------
/example/hands-on/system/logs/README.md:
--------------------------------------------------------------------------------
1 | for save logs directory.
2 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 | --format documentation
4 |
--------------------------------------------------------------------------------
/template/new/services/README.md:
--------------------------------------------------------------------------------
1 | for services page and parts objects.
2 |
--------------------------------------------------------------------------------
/template/new/system/evidences/README.md:
--------------------------------------------------------------------------------
1 | for save evidence directory.
2 |
--------------------------------------------------------------------------------
/example/bucky-management/system/logs/README.md:
--------------------------------------------------------------------------------
1 | for save logs directory.
2 |
--------------------------------------------------------------------------------
/example/hands-on/services/README.md:
--------------------------------------------------------------------------------
1 | for services page and parts objects.
2 |
--------------------------------------------------------------------------------
/example/hands-on/system/evidences/README.md:
--------------------------------------------------------------------------------
1 | for save evidence directory.
2 |
--------------------------------------------------------------------------------
/lib/bucky.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/version'
4 |
--------------------------------------------------------------------------------
/template/new/.bucky_home:
--------------------------------------------------------------------------------
1 | # Bucky home directory.
2 | # Do not delete this file.
3 |
--------------------------------------------------------------------------------
/example/bucky-management/services/README.md:
--------------------------------------------------------------------------------
1 | for services page and parts objects.
2 |
--------------------------------------------------------------------------------
/example/bucky-management/system/evidences/README.md:
--------------------------------------------------------------------------------
1 | for save evidence directory.
2 |
--------------------------------------------------------------------------------
/example/hands-on/.bucky_home:
--------------------------------------------------------------------------------
1 | # bucky home directory.
2 | # Do not delete this file.
3 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/system/logs/README.md:
--------------------------------------------------------------------------------
1 | for save logs directory.
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 | gemspec
5 |
--------------------------------------------------------------------------------
/example/hands-on/system/evidences/screen_shots/README.md:
--------------------------------------------------------------------------------
1 | for save screen shots directory.
2 |
--------------------------------------------------------------------------------
/spec/utils/config_not_in_template.yml:
--------------------------------------------------------------------------------
1 | :config_not_in_template: 'rspec_config_not_in_template'
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/README.md:
--------------------------------------------------------------------------------
1 | for services page and parts objects.
2 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/system/evidences/README.md:
--------------------------------------------------------------------------------
1 | for save evidence directory.
2 |
--------------------------------------------------------------------------------
/template/new/system/evidences/screen_shots/README.md:
--------------------------------------------------------------------------------
1 | for save screen shots directory.
2 |
--------------------------------------------------------------------------------
/example/bucky-management/.bucky_home:
--------------------------------------------------------------------------------
1 | # Bucky home directory.
2 | # Do not delete this file.
3 |
--------------------------------------------------------------------------------
/spec/test_config/test_config.yml:
--------------------------------------------------------------------------------
1 | :test_config:
2 | :bucky_test:
3 | :config_test: 'test'
4 |
--------------------------------------------------------------------------------
/example/bucky-management/system/evidences/screen_shots/README.md:
--------------------------------------------------------------------------------
1 | for save screen shots directory.
2 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/.bucky_home:
--------------------------------------------------------------------------------
1 | # bucky home directory.
2 | # Do not delete this file.
3 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/system/evidences/screen_shots/README.md:
--------------------------------------------------------------------------------
1 | for save screen shots directory.
2 |
--------------------------------------------------------------------------------
/spec/utils/bucky_config.yml:
--------------------------------------------------------------------------------
1 | :log_path: './spec/utils/'
2 | :screen_shot_path: './system/screen_shots/'
3 | :bucky_error: 'bucky_error'
--------------------------------------------------------------------------------
/spec/test_config/hierarchy_config/test_config_hierarchy.yml:
--------------------------------------------------------------------------------
1 | :test_config:
2 | :bucky_test:
3 | :config_test: 'test_hierarchy'
4 |
--------------------------------------------------------------------------------
/lib/bucky/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Bucky
4 | module Version
5 | VERSION = '0.10.25'
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/example/hands-on/services/bucky_hands_on/pc/parts/github_search_list.yml:
--------------------------------------------------------------------------------
1 | bucky_core_a_tag:
2 | - xpath
3 | - //a[contains(text(),'lifull-dev')]
4 |
--------------------------------------------------------------------------------
/spec/utils/linkstatus_config.yml:
--------------------------------------------------------------------------------
1 | :linkstatus_open_timeout: 60
2 | :linkstatus_read_timeout: 60
3 | :linkstatus_parallel_num: 1
4 | :linkstatus_thread_num: 4
--------------------------------------------------------------------------------
/example/bucky-management/services/bucky_example/pc/parts/github_search_list.yml:
--------------------------------------------------------------------------------
1 | bucky_core_a_tag:
2 | - xpath
3 | - //a[contains(text(),'lifull-dev')]
4 |
--------------------------------------------------------------------------------
/example/hands-on/config/linkstatus_config.yml:
--------------------------------------------------------------------------------
1 | :linkstatus_parallel_num: 1
2 | :linkstatus_thread_num: 4
3 | :linkstatus_open_timeout: 60
4 | :linkstatus_read_timeout: 60
--------------------------------------------------------------------------------
/template/new/config/linkstatus_config.yml:
--------------------------------------------------------------------------------
1 | :linkstatus_parallel_num: 1
2 | :linkstatus_thread_num: 4
3 | :linkstatus_open_timeout: 60
4 | :linkstatus_read_timeout: 60
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/example/bucky-management/config/linkstatus_config.yml:
--------------------------------------------------------------------------------
1 | :linkstatus_parallel_num: 1
2 | :linkstatus_thread_num: 4
3 | :linkstatus_open_timeout: 60
4 | :linkstatus_read_timeout: 60
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/sp/parts/index.yml:
--------------------------------------------------------------------------------
1 | link:
2 | - xpath
3 | - "//*[@id='test_link']"
4 | ua:
5 | - xpath
6 | - "//*[@id='user_agent']"
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/tablet/parts/index.yml:
--------------------------------------------------------------------------------
1 | link:
2 | - xpath
3 | - "//*[@id='test_link']"
4 | ua:
5 | - xpath
6 | - "//*[@id='user_agent']"
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bundler/gem_tasks'
4 | require 'rspec/core/rake_task'
5 |
6 | RSpec::Core::RakeTask.new(:spec)
7 |
8 | task default: :spec
9 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/config/linkstatus_config.yml:
--------------------------------------------------------------------------------
1 | :linkstatus_parallel_num: 1
2 | :linkstatus_thread_num: 4
3 | :linkstatus_open_timeout: 60
4 | :linkstatus_read_timeout: 60
--------------------------------------------------------------------------------
/.circleci/setup_rubygems.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eu
3 |
4 | mkdir ~/.gem
5 | echo -e "---\r\n:rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials
6 | chmod 0600 /home/circleci/.gem/credentials
7 |
--------------------------------------------------------------------------------
/spec/test_code/services/service_a/pc/parts/sample_page.yml:
--------------------------------------------------------------------------------
1 | # Sample ====================
2 | freewod_form:
3 | - xpath
4 | - "//*[@id='freeword']"
5 | photos:
6 | - xpaths
7 | - "//*[@class='photo']"
8 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/parts/index.yml:
--------------------------------------------------------------------------------
1 | link:
2 | - xpath
3 | - "//*[@id='test_link']"
4 | ua:
5 | - xpath
6 | - "//*[@id='user_agent']"
7 | links:
8 | - css
9 | - '.links'
--------------------------------------------------------------------------------
/docker/nginx/public/test_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Test Page
6 |
7 |
8 |
9 | This is a Test page
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example/bucky-management/config/test_db_config.yml:
--------------------------------------------------------------------------------
1 | :test_db:
2 | :bucky_test:
3 | :username: root
4 | :password: password
5 | :database: bucky_test
6 | :host: 127.0.0.1
7 | :port: 3306
8 | :adapter: :mysql
9 |
--------------------------------------------------------------------------------
/spec/utils/test_db_config.yml:
--------------------------------------------------------------------------------
1 | :test_db:
2 | :bucky_test:
3 | :username: 'db_username'
4 | :password: 'db_password'
5 | :database: 'bucky_development'
6 | :host: 'loaclhost'
7 | :port: 3306
8 | :adapter: :mysql
--------------------------------------------------------------------------------
/example/hands-on/services/bucky_hands_on/pc/parts/github_top.yml:
--------------------------------------------------------------------------------
1 | search_bar:
2 | - xpath
3 | - //input[contains(@class,'header-search-input')]
4 | search_resault:
5 | - xpath
6 | - //div[contains(@class,'jump-to-suggestions')]
7 |
--------------------------------------------------------------------------------
/example/bucky-management/services/bucky_example/pc/parts/github_top.yml:
--------------------------------------------------------------------------------
1 | search_bar:
2 | - xpath
3 | - //input[contains(@class,'header-search-input')]
4 | search_resault:
5 | - xpath
6 | - //div[contains(@class,'jump-to-suggestions')]
7 |
--------------------------------------------------------------------------------
/docker/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:1.13
2 | RUN rm -f /etc/nginx/conf.d/*
3 | RUN rm -f /usr/share/nginx/html/*
4 |
5 | COPY ./public /app/public
6 | ADD ./nginx.conf /etc/nginx/nginx.conf
7 | CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
8 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | ## You can check the example code in each directory
2 | * [hands-on](https://github.com/lifull-dev/bucky-core/blob/master/example/hands-on)
3 | * [bucky-management](https://github.com/lifull-dev/bucky-core/blob/master/example/bucky-management)
4 |
--------------------------------------------------------------------------------
/template/new/config/bucky_config.yml:
--------------------------------------------------------------------------------
1 | :test_result_path: './system/test_results/'
2 | :log_path: './system/logs/'
3 | :screen_shot_path: './system/evidences/screen_shots/'
4 | :bucky_error: 'bucky_error'
5 | :test_code_repo: 'https://github.com/hogehoge/fugafuga/'
6 |
--------------------------------------------------------------------------------
/example/hands-on/config/bucky_config.yml:
--------------------------------------------------------------------------------
1 | :test_result_path: './system/test_results/'
2 | :log_path: './system/logs/'
3 | :screen_shot_path: './system/evidences/screen_shots/'
4 | :bucky_error: 'bucky_error'
5 | :test_code_repo: 'https://github.com/hogehoge/fugafuga/'
6 |
--------------------------------------------------------------------------------
/spec/test_equipment/verifications/abst_verification_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/verifications/abst_verification'
4 |
5 | describe Bucky::TestEquipment::Verifications::AbstVerification do
6 | end
7 |
--------------------------------------------------------------------------------
/example/bucky-management/config/bucky_config.yml:
--------------------------------------------------------------------------------
1 | :test_result_path: './system/test_results/'
2 | :log_path: './system/logs/'
3 | :screen_shot_path: './system/evidences/screen_shots/'
4 | :bucky_error: 'bucky_error'
5 | :test_code_repo: 'https://github.com/hogehoge/fugafuga/'
6 |
--------------------------------------------------------------------------------
/spec/version_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 | require_relative '../lib/bucky/version'
5 |
6 | describe Bucky::Version do
7 | it 'has a version number' do
8 | expect(Bucky::Version::VERSION).not_to be_empty
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/config/bucky_config.yml:
--------------------------------------------------------------------------------
1 | :test_result_path: './system/test_results/'
2 | :log_path: './system/logs/'
3 | :screen_shot_path: './system/evidences/screen_shots/'
4 | :bucky_error: 'bucky_error'
5 | :test_code_repo: 'https://github.com/hogehoge/fugafuga/'
6 |
--------------------------------------------------------------------------------
/template/make_page/sp/parts/sample_page.yml:
--------------------------------------------------------------------------------
1 | # add some parts for this page
2 |
3 | # Sample ====================
4 | # freewod_form:
5 | # - xpath
6 | # - "//*[@id='freeword']"
7 | # photos:
8 | # - css
9 | # - '.photos'
10 | # icon:
11 | # - id
12 | # - icon
--------------------------------------------------------------------------------
/template/make_page/pc/parts/sample_page.yml:
--------------------------------------------------------------------------------
1 | # Add some parts for this page
2 |
3 | # Sample ====================
4 | # freewod_form:
5 | # - xpath
6 | # - "//*[@id='freeword']"
7 | # photos:
8 | # - css
9 | # - '.photos'
10 | # icon:
11 | # - id
12 | # - icon
13 |
--------------------------------------------------------------------------------
/template/make_page/tablet/parts/sample_page.yml:
--------------------------------------------------------------------------------
1 | # Add some parts for this page
2 |
3 | # Sample ====================
4 | # freewod_form:
5 | # - xpath
6 | # - "//*[@id='freeword']"
7 | # photos:
8 | # - css
9 | # - '.photos'
10 | # icon:
11 | # - id
12 | # - icon
--------------------------------------------------------------------------------
/spec/test_code/services/service_a/sp/scenarios/linkstatus/scenario_c.yml:
--------------------------------------------------------------------------------
1 | desc: status check
2 | device: sp
3 | service: service_c
4 | priority: high
5 | test_category: linkstatus
6 | cases:
7 | - desc: top page
8 | urls:
9 | - http://example.com
10 | - http://example.com/fugafuga/
--------------------------------------------------------------------------------
/template/new/config/test_db_config.yml:
--------------------------------------------------------------------------------
1 | :test_db:
2 | :bucky_test:
3 | :username: <%= ENV['BUCKY_DB_USERNAME'] %>
4 | :password: <%= ENV['BUCKY_DB_PASSWORD'] %>
5 | :database: 'bucky_development'
6 | :host: <%= ENV['BUCKY_DB_HOSTNAME'] %>
7 | :port: 3306
8 | :adapter: :mysql
9 |
--------------------------------------------------------------------------------
/example/hands-on/config/test_db_config.yml:
--------------------------------------------------------------------------------
1 | :test_db:
2 | :bucky_test:
3 | :username: <%= ENV['BUCKY_DB_USERNAME'] %>
4 | :password: <%= ENV['BUCKY_DB_PASSWORD'] %>
5 | :database: 'bucky_development'
6 | :host: <%= ENV['BUCKY_DB_HOSTNAME'] %>
7 | :port: 3306
8 | :adapter: :mysql
9 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/config/test_db_config.yml:
--------------------------------------------------------------------------------
1 | :test_db:
2 | :bucky_test:
3 | :username: <%= ENV['BUCKY_DB_USERNAME'] %>
4 | :password: <%= ENV['BUCKY_DB_PASSWORD'] %>
5 | :database: 'bucky_development'
6 | :host: <%= ENV['BUCKY_DB_HOST'] %>
7 | :port: 3306
8 | :adapter: :mysql
9 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/verifications/abst_verification.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test/unit'
4 |
5 | module Bucky
6 | module TestEquipment
7 | module Verifications
8 | class AbstVerification
9 | include Test::Unit::Assertions
10 | end
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/sp/scenarios/linkstatus/sp_link.yml:
--------------------------------------------------------------------------------
1 | desc: sp linkstatus test suites
2 | device: sp
3 | service: service_a
4 | priority: high
5 | test_category: linkstatus
6 | cases:
7 | - case_name: sp_link_1
8 | desc: sp linkstatus test 1
9 | urls:
10 | - http://bucky.net
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Git
2 | .gitignore
3 |
4 | # CI
5 | .codeclimate.yml
6 | .circleci
7 | .rubocop*
8 |
9 | # Docker
10 | # docker-compose*
11 |
12 | # Test
13 | spec/
14 | .rspec
15 | system-testing/
16 | docker/
17 |
18 | # Log
19 | *.log
20 |
21 | # Others
22 | .DS_Store
23 | .bundle
24 | .vscode
25 | .yardoc
26 | coverage/
27 | example/
28 | doc
29 | tags
30 | tmp
31 | .sample/
32 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/user_operation/user_operation_logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Bucky
4 | module TestEquipment
5 | module UserOperation
6 | class UserOperationLogger
7 | class << self
8 | def get_user_opr_log(op_args)
9 | op_args.to_s << "\n"
10 | end
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/docker/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | user nginx;
2 |
3 | events {
4 | worker_connections 128;
5 | }
6 |
7 | http {
8 | access_log /var/log/nginx/access.log;
9 | error_log /var/log/nginx/error.log;
10 |
11 | server_tokens off;
12 | index index.html;
13 |
14 | server {
15 | listen 80;
16 | server_name localhost;
17 |
18 | location /{
19 | root /app/public;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/spec/test_code/services/service_a/pc/verifications/sample_page.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/verifications/e2e_verification'
4 |
5 | module Services
6 | module ServiceA
7 | module Pc
8 | module Verifications
9 | class SamplePage < Bucky::TestEquipment::Verifications::E2eVerification
10 | end
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/linkstatus/pc_link_exclude_asterisk.yml:
--------------------------------------------------------------------------------
1 | desc: pc linkstatus test suites
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: linkstatus
6 | exclude_urls:
7 | - http://bucky.net/test*
8 | cases:
9 | - case_name: pc_link_exclude_asterisk_1
10 | desc: pc linkstatus test 1
11 | urls:
12 | - http://bucky.net
13 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/linkstatus/pc_link_exclude_regex.yml:
--------------------------------------------------------------------------------
1 | desc: pc linkstatus test suites
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: linkstatus
6 | exclude_urls:
7 | - /http://bucky.net/.*_page.*/
8 | cases:
9 | - case_name: pc_link_exclude_regex_1
10 | desc: pc linkstatus test 1
11 | urls:
12 | - http://bucky.net
13 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/linkstatus/pc_link_exclude_normal.yml:
--------------------------------------------------------------------------------
1 | desc: pc linkstatus test suites
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: linkstatus
6 | exclude_urls:
7 | - http://bucky.net/test_page.html
8 | cases:
9 | - case_name: pc_link_exclude_normal_1
10 | desc: pc linkstatus test 1
11 | urls:
12 | - http://bucky.net
13 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/sp/pageobject/index.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require '/bucky-core/lib/bucky/test_equipment/pageobject/base_pageobject'
4 |
5 | module Services
6 | module ServiceA
7 | module Sp
8 | module PageObject
9 | class Index < Bucky::TestEquipment::PageObject::BasePageObject
10 | end
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/tablet/pageobject/index.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require '/bucky-core/lib/bucky/test_equipment/pageobject/base_pageobject'
4 |
5 | module Services
6 | module ServiceA
7 | module Tablet
8 | module PageObject
9 | class Index < Bucky::TestEquipment::PageObject::BasePageObject
10 | end
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require 'bundler/setup'
5 | require 'bucky/core'
6 |
7 | # You can add fixtures and/or initialization code here to make experimenting
8 | # with your gem easier. You can also use a different console, if you like.
9 |
10 | # (If you use this, don't forget to add pry to your Gemfile!)
11 | # require "pry"
12 | # Pry.start
13 |
14 | require 'irb'
15 | IRB.start
16 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/linkstatus/pc_link.yml:
--------------------------------------------------------------------------------
1 | desc: pc linkstatus test suites
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: linkstatus
6 | cases:
7 | - case_name: pc_link_1
8 | desc: pc linkstatus test 1
9 | urls:
10 | - http://bucky.net
11 | - case_name: pc_link_2
12 | desc: pc linkstatus test 2
13 | urls:
14 | - http://bucky-error.net
--------------------------------------------------------------------------------
/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | bucky-core:
4 | build:
5 | context: .
6 | dockerfile: Dockerfile.dev
7 | container_name: bucky-core
8 | volumes:
9 | - .:/bucky-core
10 | - .sample:/app
11 | tty: true
12 | chrome:
13 | image: selenium/standalone-chrome-debug:3.141.59-oxygen
14 | container_name: chrome
15 | ports:
16 | - '4444:4444'
17 | - '5901:5900'
18 | shm_size: 1G
--------------------------------------------------------------------------------
/spec/test_code/services/service_a/pc/scenarios/e2e/scenario_a.yml:
--------------------------------------------------------------------------------
1 | desc: suite description
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | cases:
7 | - desc: open toppage and title check
8 | procs:
9 | - proc: open toppage
10 | exec:
11 | operate: go
12 | url: http://example.com/
13 | - proc: check title
14 | exec:
15 | verify: assert_title
16 | expect: 'TItle : hogehoge'
--------------------------------------------------------------------------------
/spec/test_code/services/service_a/sp/scenarios/e2e/scenario_b.yml:
--------------------------------------------------------------------------------
1 | desc: suite description
2 | device: sp
3 | service: service_b
4 | priority: middle
5 | test_category: e2e
6 | cases:
7 | - desc: open toppage and title check
8 | procs:
9 | - proc: open toppage
10 | exec:
11 | operate: go
12 | url: http://example.com/
13 | - proc: check title
14 | exec:
15 | verify: assert_title
16 | expect: 'TItle : hogehoge'
--------------------------------------------------------------------------------
/example/hands-on/services/bucky_hands_on/pc/scenarios/linkstatus/github_top.yml:
--------------------------------------------------------------------------------
1 | # Describe for this test suite
2 | desc: Check all a tag in githib top
3 | device: pc
4 | service: bucky_hands_on
5 | priority: high
6 | test_category: linkstatus
7 | labels:
8 | - example
9 | # You can exclude url that you don't want to check
10 | exclude_urls:
11 | - https://github.com/customer-stories/*
12 | cases:
13 | - case_name: github_top_1
14 | desc: Check github top
15 | urls:
16 | - https://github.com/
17 |
--------------------------------------------------------------------------------
/lib/bucky/utils/bucky_output.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Bucky
4 | module Utils
5 | module BuckyOutput
6 | module StringColorize
7 | refine String do
8 | def black
9 | "\e[30m#{self}\e[0m"
10 | end
11 |
12 | def bg_red
13 | "\e[41m#{self}\e[0m"
14 | end
15 |
16 | def bg_green
17 | "\e[42m#{self}\e[0m"
18 | end
19 | end
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/example/bucky-management/services/bucky_example/pc/scenarios/linkstatus/github_top.yml:
--------------------------------------------------------------------------------
1 | # Describe for this test suite
2 | desc: Check all a tag in githib top
3 | device: pc
4 | service: bucky_example
5 | priority: high
6 | test_category: linkstatus
7 | labels:
8 | - example
9 | # You can exclude url that you don't want to check
10 | exclude_urls:
11 | - https://github.com/customer-stories/*
12 | cases:
13 | - case_name: github_top_1
14 | desc: Check github top
15 | urls:
16 | - https://github.com/
17 |
--------------------------------------------------------------------------------
/spec/tools/config_spec.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: 'docker_host_ip' # Default selenium ip. please set ip or 'docker_host_ip'. ip route | awk 'NR==1 {print $3}'
2 | :selenium_port: <%= ENV['SELENIUM_PORT'] || 4444 %>
3 | :browser: :chrome
4 | :sp_device_name: :iphone
5 | :tablet_device_name: :ipad
6 | :device_name_on_chrome:
7 | :iphone: 'iPhone X'
8 | :android: 'Galaxy S5'
9 | # :ipad: 'iPad'
10 | :user_agent: 'QA-E2E (X11; Linux x86_64)'
11 | :driver_read_timeout: 120
12 | :driver_open_timeout: 90
13 | :find_element_timeout: 60
--------------------------------------------------------------------------------
/spec/tools/rule_config_spec.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: 'docker_host_ip' # default selenium ip. please set ip or 'docker_host_ip'. ip route | awk 'NR==1 {print $3}'
2 | :selenium_port: <%= ENV['SELENIUM_PORT'] || 4444 %>
3 | :browser: :chrome
4 | :sp_device_name: :iphone
5 | :tablet_device_name: :ipad
6 | :device_name_on_chrome:
7 | :iphone: 'iPhone X'
8 | :android: 'Galaxy S5'
9 | :ipad: 'iPad'
10 | :user_agent: 'QA-E2E (X11; Linux x86_64)'
11 | :driver_read_timeout: 120
12 | :driver_open_timeout: 90
13 | :find_element_timeout: 60
--------------------------------------------------------------------------------
/lib/bucky/core/test_core/exit_handler.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'singleton'
4 |
5 | module Bucky
6 | module Core
7 | module TestCore
8 | class ExitHandler
9 | include Singleton
10 |
11 | def initialize
12 | @exit_code = 0
13 | end
14 |
15 | def reset
16 | @exit_code = 0
17 | end
18 |
19 | def raise
20 | @exit_code = 1
21 | end
22 |
23 | def bucky_exit
24 | exit @exit_code
25 | end
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.2-alpine
2 | ENV LANG ja_JP.UTF-8
3 | ENV PAGER busybox less
4 |
5 | RUN apk update && \
6 | apk upgrade && \
7 | apk add --update\
8 | bash \
9 | build-base \
10 | curl-dev \
11 | git \
12 | iproute2 \
13 | libxml2-dev \
14 | libxslt-dev \
15 | linux-headers \
16 | mysql-dev \
17 | openssh \
18 | ruby-dev \
19 | ruby-json \
20 | tzdata \
21 | yaml \
22 | yaml-dev \
23 | zlib-dev
24 |
25 | RUN gem install bucky-core
26 |
27 | WORKDIR /app
28 | RUN chown -R nobody:nobody /app
29 | USER nobody
30 |
--------------------------------------------------------------------------------
/spec/test_equipment/user_operation/user_operation_logger_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/user_operation/user_operation_logger'
4 |
5 | describe Bucky::TestEquipment::UserOperation::UserOperationLogger do
6 | subject { Bucky::TestEquipment::UserOperation::UserOperationLogger }
7 |
8 | describe '.get_user_opr_log' do
9 | let(:operation_args) { { operation: :go } }
10 | it 'change to string' do
11 | expect(subject.get_user_opr_log(operation_args)).to be_a_kind_of(String)
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/pageobject/index.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require '/bucky-core/lib/bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module ServiceA
6 | module Pc
7 | module PageObject
8 | class Index < Bucky::TestEquipment::PageObject::BasePageObject
9 | def click_single_element(_)
10 | links.click
11 | end
12 |
13 | def click_multiple_element(_)
14 | links[1].click
15 | end
16 | end
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/setup_each_pc_e2e.yml:
--------------------------------------------------------------------------------
1 | desc: setup each pc e2e suits
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | setup_each:
7 | procs:
8 | - proc: open index
9 | exec:
10 | operate: go
11 | url: http://bucky.net
12 | cases:
13 | - case_name: setup_each_pc_e2e_1
14 | func: setup each pc e2e 1 func
15 | desc: setup each pc e2e 1 func
16 | procs:
17 | - proc: check title
18 | exec:
19 | verify: assert_title
20 | expect: Test Index
--------------------------------------------------------------------------------
/spec/utils/bucky_output_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../lib/bucky/utils/bucky_output'
4 |
5 | describe Bucky::Utils::BuckyOutput do
6 | describe 'StringColorize' do
7 | using Bucky::Utils::BuckyOutput::StringColorize
8 | let(:string) { 'Hoge' }
9 |
10 | it '.black' do
11 | expect(string.black).to eql "\e[30m#{string}\e[0m"
12 | end
13 |
14 | it '.bg_green' do
15 | expect(string.bg_green).to eql "\e[42m#{string}\e[0m"
16 | end
17 |
18 | it '.bg_red' do
19 | expect(string.bg_red).to eql "\e[41m#{string}\e[0m"
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/utils/e2e_config.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: 'docker_host_ip' # Default selenium ip. please set ip or 'my_ssh_ip'.
2 | :selenium_port: '4444' # Default selenium port
3 | :selenium_mode: :remote # Other selection: :local(only PC mode), :headless
4 | :browser: :chrome # Other selection: :firefox (only PC mode)
5 | :headless: false
6 | :e2e_parallel_num: 1
7 | :sp_device_name: :iphone6 # Other selection: nexus5
8 | :device_chrome_name:
9 | :iphone6: 'Apple iPhone 6'
10 | :nexus5: 'Google Nexus 5'
11 | :chromedriver_flags: # If you want to use chromedriver options, please add like below
12 | # - '--disable-dev-shm-usage'
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/teardown_each_pc_e2e.yml:
--------------------------------------------------------------------------------
1 | desc: teardown each pc e2e suits
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | teardown_each:
7 | procs:
8 | - proc: open test_page.html
9 | exec:
10 | operate: go
11 | url: http://bucky.net/test_page.html
12 | cases:
13 | - case_name: teardown_each_pc_e2e_1
14 | func: teardown each pc e2e 1 func
15 | desc: teardown each pc e2e 1 func
16 | procs:
17 | - proc: open page
18 | exec:
19 | operate: go
20 | url: http://bucky.net
--------------------------------------------------------------------------------
/.circleci/deploy_to_rubygems.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eu
3 |
4 | VERSION=$(git describe --tags | grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1)
5 | git config user.email "bucky-operator@users.noreply.github.com"
6 | git config user.name "bucky-operator"
7 | # Update version.rb
8 | sed -i -e "s/VERSION = '[0-9]\+\.[0-9]\+\.[0-9]\+'/VERSION = '$VERSION'/" lib/bucky/version.rb
9 | git diff
10 | git checkout master
11 | git add lib/bucky/version.rb
12 | git commit -m "Version $VERSION"
13 | # Build and release gem
14 | gem build bucky-core.gemspec
15 | gem push "bucky-core-$VERSION.gem"
16 | # Push to master
17 | git push origin master
18 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/verifications/index.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require '/bucky-core/lib/bucky/test_equipment/verifications/e2e_verification'
4 | module Services
5 | module ServiceA
6 | module Pc
7 | module Verifications
8 | class Index < Bucky::TestEquipment::Verifications::E2eVerification
9 | def click_single_element(_)
10 | @pages.index.links.click
11 | end
12 |
13 | def click_multiple_element(_)
14 | @pages.index.links[1].click
15 | end
16 | end
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Dir[File.join(File.dirname(__FILE__), '../lib/**/*.rb')].each { |f| require f }
4 | require 'simplecov'
5 | require 'simplecov-console'
6 |
7 | # Save to CircleCI's artifacts directory if we're on CircleCI
8 | if ENV['CIRCLE_ARTIFACTS']
9 | dir = File.join(ENV['CIRCLE_ARTIFACTS'], 'coverage')
10 | SimpleCov.coverage_dir(dir)
11 | end
12 |
13 | SimpleCov::Formatter::Console.table_options = { max_width: 170 }
14 | SimpleCov.formatter = SimpleCov::Formatter::Console
15 |
16 | SimpleCov.start do
17 | add_filter 'spec/'
18 | end
19 |
20 | $LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
21 |
--------------------------------------------------------------------------------
/template/make_page/pc/pageobject/sample_page.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module {SampleService}
6 | module Pc
7 | module PageObject
8 | class {SamplePage} < Bucky::TestEquipment::PageObject::BasePageObject
9 | protected
10 |
11 | # Add some user operations for this page
12 | # Sample ====================
13 | # @param [Hash] args
14 | # def search_freeword(**args)
15 | # freeword_form.send_keys(args[:word])
16 | # freeword_form.submit
17 | # end
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/test_code/services/service_a/pc/pageobject/sample_page.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module ServiceA
6 | module Pc
7 | module PageObject
8 | class SamplePage < Bucky::TestEquipment::PageObject::BasePageObject
9 | protected
10 |
11 | # Add some user operations for this page
12 | # Sample ====================
13 | # @param [Hash] args
14 | # def search_freeword(**args)
15 | # freeword_form.send_keys(args[:word])
16 | # freeword_form.submit
17 | # end
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/template/make_page/sp/pageobject/sample_page.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 |
5 | module Services
6 | module {SampleService}
7 | module Sp
8 | module PageObject
9 | class {SamplePage} < Bucky::TestEquipment::PageObject::BasePageObject
10 | protected
11 |
12 | # Add some user operations for this page
13 | # Sample ====================
14 | # @param [Hash] args
15 | # def search_freeword(**args)
16 | # freeword_form.send_keys(args[:word])
17 | # freeword_form.submit
18 | # end
19 | end
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/example/hands-on/services/bucky_hands_on/pc/pageobject/github_top.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module BuckyHandsOn
6 | module Pc
7 | module PageObject
8 | class GithubTop < Bucky::TestEquipment::PageObject::BasePageObject
9 | protected
10 |
11 | # add some user operations for this page
12 | # sample ====================
13 | # @param [Hash] args
14 | # def search_freeword(**args)
15 | # freeword_form.send_keys(args[:word])
16 | # freeword_form.submit
17 | # end
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/utils/yaml_load_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../lib/bucky/utils/yaml_load'
4 |
5 | describe Bucky::Utils::YamlLoad do
6 | let(:yaml_load) { Class.new { extend Bucky::Utils::YamlLoad } }
7 | let(:yaml_file) { './spec/test_config/test_config.yml' }
8 | let(:yaml_dir) { './spec/test_config//**/*yml' }
9 |
10 | describe '#load_yaml' do
11 | it 'load target yaml' do
12 | expect(yaml_load.load_yaml(yaml_file)).to be_a(Hash)
13 | end
14 | end
15 |
16 | describe '#file_sort_hierarchy' do
17 | it 'sort and deepest file last' do
18 | expect(yaml_load.file_sort_hierarchy(yaml_dir).last).to include('hierarchy_config')
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/template/make_page/tablet/pageobject/sample_page.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 |
5 | module Services
6 | module {SampleService}
7 | module Tablet
8 | module PageObject
9 | class {SamplePage} < Bucky::TestEquipment::PageObject::BasePageObject
10 | protected
11 |
12 | # Add some user operations for this page
13 | # Sample ====================
14 | # @param [Hash] args
15 | # def search_freeword(**args)
16 | # freeword_form.send_keys(args[:word])
17 | # freeword_form.submit
18 | # end
19 | end
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw[pon]
2 | tags
3 | bak_*
4 | tmp_*
5 | *.log
6 | *.png
7 | *.json
8 | vendor/
9 | .bundle
10 |
11 | ### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/ruby.gitignore
12 |
13 | *.gem
14 | *.rbc
15 | /.config
16 | /coverage/
17 | /InstalledFiles
18 | /pkg/
19 | /spec/reports/
20 | /spec/examples.txt
21 | /test/tmp/
22 | /test/version_tmp/
23 | /tmp/
24 |
25 | ## Documentation cache and generated files:
26 | /.yardoc/
27 | /_yardoc/
28 | /doc/
29 | /rdoc/
30 |
31 | ## Environment normalization:
32 | /.bundle/
33 | /vendor/bundle
34 | /lib/bundler/man/
35 |
36 | # Unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
37 | .rvmrc
38 |
39 | .DS_Store
40 | .sample
41 |
--------------------------------------------------------------------------------
/docker/nginx/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Test Index
6 |
7 |
12 |
13 |
14 | Go to test page
15 |
16 |
19 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/example/bucky-management/services/bucky_example/pc/pageobject/github_top.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module BuckyExample
6 | module Pc
7 | module PageObject
8 | class GithubTop < Bucky::TestEquipment::PageObject::BasePageObject
9 | protected
10 |
11 | # Add some user operations for this page
12 | # Sample ====================
13 | # @param [Hash] args
14 | # def search_freeword(**args)
15 | # freeword_form.send_keys(args[:word])
16 | # freeword_form.submit
17 | # end
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/example/hands-on/config/e2e_config.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: localhost # default selenium ip. please set ip or 'docker_host_ip'. ip route | awk 'NR==1 {print $3}'
2 | :selenium_port: '4444' # default selenium port
3 | :browser: :chrome # Only chrome
4 | :headless: false
5 | :e2e_parallel_num: 1
6 | :sp_device_name: :iphone
7 | :tablet_device_name: :ipad
8 | :device_name_on_chrome:
9 | :iphone: 'iPhone X'
10 | :android: 'Galaxy S5'
11 | :ipad: 'iPad'
12 | :chromedriver_flags: # If you want to use chromedriver options, please add like below
13 | # - '--disable-dev-shm-usage'
14 | :user_agent: 'E2ETest (X11; Linux x86_64)'
15 | :driver_read_timeout: 60 # sec
16 | :driver_open_timeout: 60 # sec
17 | :find_element_timeout: 10 # sec
--------------------------------------------------------------------------------
/example/hands-on/services/bucky_hands_on/pc/pageobject/github_search_list.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module BuckyHandsOn
6 | module Pc
7 | module PageObject
8 | class GithubSearchList < Bucky::TestEquipment::PageObject::BasePageObject
9 | protected
10 |
11 | # add some user operations for this page
12 | # sample ====================
13 | # @param [Hash] args
14 | # def search_freeword(**args)
15 | # freeword_form.send_keys(args[:word])
16 | # freeword_form.submit
17 | # end
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/template/new/config/e2e_config.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: 'docker_host_ip' # Default selenium ip. please set ip or 'docker_host_ip'. ip route | awk 'NR==1 {print $3}'
2 | :selenium_port: '4444' # Default selenium port
3 | :browser: :chrome # Only chrome
4 | :headless: false
5 | :e2e_parallel_num: 1
6 | :sp_device_name: :iphone
7 | :tablet_device_name: :ipad
8 | :device_name_on_chrome:
9 | :iphone: 'iPhone X'
10 | :android: 'Galaxy S5'
11 | :ipad: 'iPad'
12 | :chromedriver_flags: # If you want to use chromedriver options, please add like below
13 | # - '--disable-dev-shm-usage'
14 | :user_agent: 'E2ETest (X11; Linux x86_64)'
15 | :driver_read_timeout: 60 # sec
16 | :driver_open_timeout: 60 # sec
17 | :find_element_timeout: 10 # sec
--------------------------------------------------------------------------------
/example/bucky-management/config/e2e_config.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: localhost # Default selenium ip. please set ip or 'docker_host_ip'. ip route | awk 'NR==1 {print $3}'
2 | :selenium_port: '4444' # Default selenium port
3 | :browser: :chrome # Only chrome
4 | :headless: false
5 | :e2e_parallel_num: 1
6 | :sp_device_name: :iphone
7 | :tablet_device_name: :ipad
8 | :device_name_on_chrome:
9 | :iphone: 'iPhone X'
10 | :android: 'Galaxy S5'
11 | :ipad: 'iPad'
12 | :chromedriver_flags: # If you want to use chromedriver options, please add like below
13 | # - '--disable-dev-shm-usage'
14 | :user_agent: 'E2ETest (X11; Linux x86_64)'
15 | :driver_read_timeout: 60 # sec
16 | :driver_open_timeout: 60 # sec
17 | :find_element_timeout: 10 # sec
--------------------------------------------------------------------------------
/example/bucky-management/services/bucky_example/pc/pageobject/github_search_list.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bucky/test_equipment/pageobject/base_pageobject'
4 | module Services
5 | module BuckyExample
6 | module Pc
7 | module PageObject
8 | class GithubSearchList < Bucky::TestEquipment::PageObject::BasePageObject
9 | protected
10 |
11 | # Add some user operations for this page
12 | # Sample ====================
13 | # @param [Hash] args
14 | # def search_freeword(**args)
15 | # freeword_form.send_keys(args[:word])
16 | # freeword_form.submit
17 | # end
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/selenium_handler/wait_handler.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'selenium-webdriver'
4 | require 'English'
5 |
6 | module Bucky
7 | module TestEquipment
8 | module SeleniumHandler
9 | module WaitHandler
10 | module_function
11 |
12 | def wait_until_helper(timeout, interval, ignore, &block)
13 | wait = Selenium::WebDriver::Wait.new(timeout: timeout, interval: interval, ignore: [ignore])
14 | wait.until { block.call }
15 | rescue Selenium::WebDriver::Error::TimeoutError
16 | raise ignore, "Wait until the limit times for #{caller[1][/`([^']*)'/, 1]}\n #{$ERROR_INFO.message}"
17 | end
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/config/e2e_config.yml:
--------------------------------------------------------------------------------
1 | :selenium_ip: 'docker_host_ip' # Default selenium ip. please set ip or 'docker_host_ip'. ip route | awk 'NR==1 {print $3}'
2 | :selenium_port: '4444' # Default selenium port
3 | :browser: :chrome # Only chrome
4 | :headless: true
5 | :e2e_parallel_num: 1
6 | :sp_device_name: :iphone
7 | :tablet_device_name: :ipad
8 | :device_name_on_chrome:
9 | :iphone: 'iPhone X'
10 | :android: 'Galaxy S5'
11 | :ipad: 'iPad'
12 | :chromedriver_flags: # If you want to use chromedriver options, please add like below
13 | # - '--disable-dev-shm-usage'
14 | :user_agent: 'E2ETest (X11; Linux x86_64)'
15 | :driver_read_timeout: 60 # sec
16 | :driver_open_timeout: 60 # sec
17 | :find_element_timeout: 10 # sec
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/test_case/linkstatus_test_case.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'net/http'
4 | require_relative '../verifications/status_checker'
5 | require_relative './abst_test_case'
6 |
7 | module Bucky
8 | module TestEquipment
9 | module TestCase
10 | class LinkstatusTestCase < Bucky::TestEquipment::TestCase::AbstTestCase
11 | include Bucky::TestEquipment::Verifications::StatusChecker
12 |
13 | class << self
14 | def startup; end
15 |
16 | def shutdown; end
17 | end
18 |
19 | def setup; end
20 |
21 | def teardown
22 | # Call abst_test_case.teardown to get elappsed time of every test case
23 | super
24 | end
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/sp/scenarios/e2e/sp_e2e_test.yml:
--------------------------------------------------------------------------------
1 | desc: sp e2e suits
2 | device: sp
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | cases:
7 | - case_name: sp_e2e_1
8 | func: sp e2e 1 func UserAgent
9 | desc: sp e2e 1 func UserAgent
10 | procs:
11 | - proc: open index
12 | exec:
13 | operate: go
14 | url: http://bucky.net
15 | - proc: check UserAgent(DeviceName)
16 | exec:
17 | verify: assert_contained_text
18 | expect: iPhone
19 | page: index
20 | part: ua
21 | - proc: check UserAgent(Browser)
22 | exec:
23 | verify: assert_contained_text
24 | expect: Safari
25 | page: index
26 | part: ua
--------------------------------------------------------------------------------
/docker-compose.dev-with-bm.yml:
--------------------------------------------------------------------------------
1 | services:
2 | bucky-core:
3 | build:
4 | context: .
5 | dockerfile: Dockerfile.dev
6 | container_name: bucky-core
7 | volumes:
8 | - .:/bucky-core
9 | - .sample:/app
10 | tty: true
11 | environment:
12 | - BUCKY_DB_USERNAME=root
13 | - BUCKY_DB_PASSWORD=password
14 | - BUCKY_DB_HOSTNAME=bm-mysql
15 | - BUCKY_DB_NAME=bucky_development
16 | networks:
17 | - bucky-management_default
18 | chrome:
19 | image: selenium/standalone-chromium:128.0
20 | container_name: chrome
21 | ports:
22 | - '4444:4444'
23 | - '5901:5900'
24 | shm_size: 1G
25 | networks:
26 | - bucky-management_default
27 | networks:
28 | bucky-management_default:
29 | external: true
30 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/tablet/scenarios/e2e/tablet_e2e_test.yml:
--------------------------------------------------------------------------------
1 | desc: tablet e2e suits
2 | device: tablet
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | cases:
7 | - case_name: tablet_e2e_1
8 | func: tablet e2e 1 func UserAgent
9 | desc: tablet e2e 1 func UserAgent
10 | procs:
11 | - proc: open index
12 | exec:
13 | operate: go
14 | url: http://bucky.net
15 | - proc: check UserAgent(DeviceName)
16 | exec:
17 | verify: assert_contained_text
18 | expect: iPad
19 | page: index
20 | part: ua
21 | - proc: check UserAgent(Browser)
22 | exec:
23 | verify: assert_contained_text
24 | expect: Safari
25 | page: index
26 | part: ua
--------------------------------------------------------------------------------
/lib/bucky/utils/yaml_load.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'yaml'
4 | require 'erb'
5 |
6 | module Bucky
7 | module Utils
8 | module YamlLoad
9 | # Load yaml(include erb)
10 | # @param [File] yaml file
11 | # @return [Hash] hashed yaml contents
12 | def load_yaml(file)
13 | YAML.safe_load(
14 | ERB.new(File.read(file)).result,
15 | permitted_classes: [Array, Hash, String, Numeric, Symbol, TrueClass, FalseClass],
16 | aliases: true
17 | )
18 | end
19 |
20 | # Sort files to hierarchy
21 | # @param [String] path of directory
22 | # @return [Array] sorted files
23 | def file_sort_hierarchy(path)
24 | Dir.glob(path).sort_by { |f| f.split('/').size }
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/docker-compose.system-test.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | bucky-core:
4 | container_name: bucky-core
5 | build:
6 | context: .
7 | dockerfile: Dockerfile.system-test
8 | volumes:
9 | - .:/bucky-core
10 | tty: true
11 | depends_on:
12 | chrome:
13 | condition: service_healthy
14 | web:
15 | condition: service_started
16 | chrome:
17 | container_name: bucky-chrome
18 | image: selenium/standalone-chromium:128.0
19 | healthcheck:
20 | test: ["CMD-SHELL", "curl -f http://localhost:4444 || exit 1"]
21 | interval: 3s
22 | timeout: 5s
23 | retries: 2
24 | ports:
25 | - '4444:4444'
26 | # For local debug
27 | - '5901:5900'
28 | web:
29 | container_name: bucky.net
30 | build:
31 | context: docker/nginx
32 |
--------------------------------------------------------------------------------
/spec/core/test_core/test_result_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/test_core/test_result'
4 |
5 | describe Bucky::Core::TestCore::TestResult do
6 | let(:test_suite_result) { [{ test_case_1: 1.234 }, { test_case_2: 2.257 }] }
7 | let(:tdo_double) { double('double of Core::Database::TestDataOperator') }
8 | let(:instance) { Bucky::Core::TestCore::TestResult.instance }
9 | before :each do
10 | allow(Bucky::Core::Database::TestDataOperator).to receive(:new).and_return(tdo_double)
11 | end
12 |
13 | describe '#save' do
14 | it 'call test_data_operator#save_test_result' do
15 | allow(instance).to receive(:format_result_summary)
16 | expect(tdo_double).to receive(:save_test_result)
17 | instance.save(test_suite_result)
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/bucky/utils/bucky_logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'logger'
4 | require_relative './config'
5 |
6 | module Bucky
7 | module Utils
8 | module BuckyLogger
9 | LogFileDir = Bucky::Utils::Config.instance.data[:log_path]
10 | # Write following logs
11 | # - user opareation (e.g. test_sample_app_pc_e2e_1_1.log)
12 | # - verification (e.g. test_sample_app_pc_e2e_1_1.log)
13 | # - Error of Bucky core (bucky_error.log)
14 | # @param [String] file_name
15 | # @param [String] or [Execption] content
16 | def write(file_name, content)
17 | puts " #{content}"
18 | logger = Logger.new("#{LogFileDir}#{file_name}.log", 1)
19 | logger.info content
20 | logger.close
21 | end
22 | module_function :write
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/bucky/core/report/screen_shot_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../utils/config'
4 | require_relative '../exception/bucky_exception'
5 |
6 | module Bucky
7 | module Core
8 | module Report
9 | module ScreenShotGenerator
10 | # Save screen shot
11 | # @param [Webdriver] driver
12 | # @param [String] test_case e.g.) test_sample_app_pc_e2e_1_1
13 | def generate_screen_shot(driver, test_case)
14 | timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
15 | driver.save_screenshot(
16 | Bucky::Utils::Config.instance[:screen_shot_path] + test_case << "_#{timestamp}.png"
17 | )
18 | rescue StandardError => e
19 | Bucky::Core::Exception::BuckyException.handle(e)
20 | end
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/bucky/core/database/db_connector.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'sequel'
4 | require_relative '../exception/bucky_exception'
5 | require_relative '../../utils/config'
6 |
7 | module Bucky
8 | module Core
9 | module Database
10 | class DbConnector
11 | attr_reader :con
12 |
13 | def initialize
14 | @test_db_config = Bucky::Utils::Config.instance[:test_db]
15 | end
16 |
17 | # Connect to database
18 | # @param [String] db_name database name
19 | def connect(db_name = 'bucky_test')
20 | @con = if $debug
21 | @test_db_config[db_name.to_sym]
22 | else
23 | Sequel.connect(@test_db_config[db_name.to_sym], encoding: 'utf8')
24 | end
25 | end
26 |
27 | # Disconnect to database
28 | def disconnect
29 | @con.disconnect
30 | end
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/test_equipment/evidence/evidence_generator_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/evidence/evidence_generator'
4 |
5 | describe Bucky::TestEquipment::Evidence::EvidenceGenerator do
6 | describe '#report' do
7 | it 'call BuckyLogger.write' do
8 | expect(Bucky::Utils::BuckyLogger).to receive(:write)
9 | subject.report('file_path', 'error_class')
10 | end
11 | end
12 | end
13 |
14 | describe Bucky::TestEquipment::Evidence::E2eEvidence do
15 | describe '#save_evidence' do
16 | it 'call #generate_screen_shot' do
17 | allow(subject).to receive(:report)
18 | expect(subject).to receive(:generate_screen_shot)
19 | subject.save_evidence('error_class')
20 | end
21 | it 'call #report' do
22 | allow(subject).to receive(:generate_screen_shot)
23 | expect(subject).to receive(:report)
24 | subject.save_evidence('error_class')
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/test_equipment/selenium_handler/wait_handler_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/selenium_handler/wait_handler'
4 | require 'selenium-webdriver'
5 | require 'English'
6 |
7 | describe Bucky::TestEquipment::SeleniumHandler::WaitHandler do
8 | subject { Bucky::TestEquipment::SeleniumHandler::WaitHandler }
9 | describe '#wait_until_helper' do
10 | let(:timeout) { 0.5 }
11 | let(:interval) { 0.1 }
12 | let(:ignore) { StandardError }
13 | let(:test_block) { p 'test_block' }
14 | let(:test_block_raise_error) { raise StandardError }
15 | it 'not raise exeption' do
16 | expect { subject.wait_until_helper(timeout, interval, ignore) { test_block } }.not_to raise_error
17 | end
18 |
19 | it 'raise ignore exeption after timeout' do
20 | expect { subject.wait_until_helper(timeout, interval, ignore) { test_block_error } }.to raise_error(StandardError)
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/Dockerfile.dev:
--------------------------------------------------------------------------------
1 | FROM ruby:3.2-alpine
2 | ENV LANG ja_JP.UTF-8
3 | ENV PAGER busybox less
4 |
5 | RUN apk update && \
6 | apk upgrade && \
7 | apk add --update\
8 | bash \
9 | build-base \
10 | curl-dev \
11 | iproute2 \
12 | git \
13 | libxml2-dev \
14 | libxslt-dev \
15 | linux-headers \
16 | mysql-dev \
17 | openssh \
18 | ruby-dev \
19 | ruby-json \
20 | tzdata \
21 | yaml \
22 | yaml-dev \
23 | zlib-dev
24 |
25 | ENV BC_DIR /bucky-core/
26 | ENV PATH /bucky-core/exe/:$PATH
27 | WORKDIR $BC_DIR
28 | COPY . $BC_DIR
29 | RUN \
30 | gem install bundler -v 2.5.18 && \
31 | echo 'gem: --no-document' >> ~/.gemrc && \
32 | cp ~/.gemrc /etc/gemrc && \
33 | chmod uog+r /etc/gemrc && \
34 | bundle config --global build.nokogiri --use-system-libraries && \
35 | bundle config --global jobs 4 && \
36 | bundle install && \
37 | rm -rf ~/.gem
38 |
39 | WORKDIR /app
40 | RUN chown -R nobody:nobody /app
41 | USER nobody
42 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/evidence/evidence_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../core/report/screen_shot_generator'
4 | require_relative '../../utils/bucky_logger'
5 |
6 | module Bucky
7 | module TestEquipment
8 | module Evidence
9 | class EvidenceGenerator
10 | include Bucky::Utils::BuckyLogger
11 |
12 | # Save log
13 | # @param [String] file
14 | # @param [Exception Object] err
15 | def report(file, err)
16 | Bucky::Utils::BuckyLogger.write(file, err)
17 | end
18 | end
19 |
20 | # Create evidence for each test category
21 | class E2eEvidence < EvidenceGenerator
22 | include Bucky::Core::Report::ScreenShotGenerator
23 |
24 | def initialize(**evid_args)
25 | @driver = evid_args[:driver]
26 | @tc = evid_args[:test_case]
27 | end
28 |
29 | def save_evidence(err)
30 | generate_screen_shot(@driver, @tc)
31 | report("#{@tc}_error", err)
32 | end
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/setup_teardown_each_pc_e2e.yml:
--------------------------------------------------------------------------------
1 | desc: setup teardown each pc e2e suits
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | setup_each:
7 | procs:
8 | - proc: open index
9 | exec:
10 | operate: go
11 | url: http://bucky.net
12 | teardown_each:
13 | procs:
14 | - proc: open test_page.html
15 | exec:
16 | operate: go
17 | url: http://bucky.net/test_page.html
18 |
19 | cases:
20 | - case_name: setup_teardown_each_pc_e2e_1
21 | func: setup teardown each pc e2e 1 func
22 | desc: setup teardown each pc e2e 1 func
23 | procs:
24 | - proc: check title
25 | exec:
26 | verify: assert_title
27 | expect: Test Index
28 | - case_name: setup_teardown_each_pc_e2e_2
29 | func: setup teardown each pc e2e 2 func
30 | desc: setup teardown each pc e2e 2 func
31 | procs:
32 | - proc: check title
33 | exec:
34 | verify: assert_title
35 | expect: Test Index
--------------------------------------------------------------------------------
/Dockerfile.system-test:
--------------------------------------------------------------------------------
1 | FROM ruby:3.2-alpine
2 | ENV LANG ja_JP.UTF-8
3 | ENV PAGER busybox less
4 |
5 | RUN apk update && \
6 | apk upgrade && \
7 | apk add --update \
8 | bash \
9 | build-base \
10 | curl-dev \
11 | git \
12 | iproute2 \
13 | libxml2-dev \
14 | libxslt-dev \
15 | linux-headers \
16 | mysql-dev \
17 | openssh \
18 | ruby-dev \
19 | ruby-json \
20 | tzdata \
21 | yaml \
22 | yaml-dev \
23 | zlib-dev \
24 | curl \
25 | parallel
26 |
27 | RUN git clone https://github.com/bats-core/bats-core.git /tmp/bats-core && \
28 | cd /tmp/bats-core && \
29 | ./install.sh /usr/local && \
30 | rm -rf /tmp/bats-core
31 |
32 | ENV BC_DIR /bucky-core/
33 | ENV PATH /bucky-core/exe/:$PATH
34 | WORKDIR $BC_DIR
35 | COPY . $BC_DIR
36 |
37 | RUN gem install bundler -v 2.5.18 && \
38 | echo 'gem: --no-document' >> ~/.gemrc && \
39 | cp ~/.gemrc /etc/gemrc && \
40 | chmod uog+r /etc/gemrc && \
41 | bundle config --global build.nokogiri --use-system-libraries && \
42 | bundle config --global jobs 4 && \
43 | bundle install && \
44 | rm -rf ~/.gem
45 |
46 | WORKDIR /app
47 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | checks:
3 | argument-count:
4 | enabled: true
5 | config:
6 | threshold: 4
7 | complex-logic:
8 | enabled: true
9 | config:
10 | threshold: 4
11 | file-lines:
12 | enabled: true
13 | config:
14 | threshold: 250
15 | method-complexity:
16 | enabled: true
17 | config:
18 | threshold: 10
19 | method-count:
20 | enabled: true
21 | config:
22 | threshold: 20
23 | method-lines:
24 | enabled: true
25 | config:
26 | threshold: 30
27 | nested-control-flow:
28 | enabled: true
29 | config:
30 | threshold: 4
31 | return-statements:
32 | enabled: true
33 | config:
34 | threshold: 4
35 | similar-code:
36 | enabled: true
37 | config:
38 | threshold: #language-specific defaults. overrides affect all languages.
39 | identical-code:
40 | enabled: true
41 | config:
42 | threshold: #language-specific defaults. overrides affect all languages.
43 | # plugins:
44 | # rubocop: # run by CircleCI
45 | # enabled: true
46 | # channel: rubocop-0-58
47 | exclude_patterns:
48 | - "spec/"
49 |
--------------------------------------------------------------------------------
/spec/core/database/db_connector_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/database/db_connector'
4 |
5 | describe Bucky::Core::Database::DbConnector do
6 | let(:config_double) { double('double of Config') }
7 |
8 | before do
9 | # Config mock
10 | allow(Bucky::Utils::Config).to receive(:instance).and_return(config_double)
11 | allow(config_double).to receive('[]').and_return(bucky_test: 'test')
12 | end
13 |
14 | describe '#connect' do
15 | it 'Call Sequel.connect' do
16 | allow(Sequel).to receive(:connect).and_return('call Sequel.connect')
17 | expect(Sequel).to receive(:connect)
18 | subject.connect('bucky_test')
19 | end
20 | end
21 |
22 | describe '#disconnect' do
23 | let(:sequel_double) { double('double of Sequel') }
24 |
25 | it 'Call Sequel.disconnect' do
26 | allow(Sequel).to receive(:connect).and_return(sequel_double)
27 | allow(sequel_double).to receive(:disconnect).and_return('call Sequel.disconnect')
28 |
29 | subject.connect
30 |
31 | expect(sequel_double).to receive(:disconnect)
32 | subject.disconnect
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/core/report/screen_shot_generator_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/report/screen_shot_generator'
4 |
5 | describe Bucky::Core::Report::ScreenShotGenerator do
6 | let(:test_class) { Struct.new(:test_class) { include Bucky::Core::Report::ScreenShotGenerator } }
7 | let(:driver) { double('double of driver') }
8 | let(:config_double) { double('double of Config') }
9 | let(:error) { StandardError }
10 | subject { test_class.new }
11 |
12 | before do
13 | allow(config_double).to receive('[]').and_return('screen_shot_path')
14 | allow(Bucky::Utils::Config).to receive(:instance).and_return(config_double)
15 | end
16 |
17 | describe '.generate_screen_shot' do
18 | it 'call Selenium::WebDriver::DriverExtensions::TakesScreenshot#save_screenshot' do
19 | expect(driver).to receive(:save_screenshot)
20 | subject.generate_screen_shot(driver, 'test_case_name')
21 | end
22 | context 'get error when calling save_screenshot' do
23 | it 'call BuckyException.handle' do
24 | allow(driver).to receive(:save_screenshot).and_raise(error)
25 | expect(Bucky::Core::Exception::BuckyException).to receive(:handle)
26 | subject.generate_screen_shot(driver, 'test_case_name')
27 | end
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/core/test_core/exit_handler_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/test_core/exit_handler'
4 |
5 | describe Bucky::Core::TestCore::ExitHandler do
6 | let(:instance) { Bucky::Core::TestCore::ExitHandler.instance }
7 |
8 | describe '#initialize' do
9 | it 'initialize exit handler' do
10 | expect(instance.instance_variable_get(:@exit_code)).to eq 0
11 | end
12 | end
13 |
14 | describe '#reset' do
15 | it 'reset exit_code to 0' do
16 | instance.instance_variable_set(:@exit_code, 1)
17 | instance.reset
18 | expect(instance.instance_variable_get(:@exit_code)).to eq 0
19 | end
20 | end
21 |
22 | describe '#raise' do
23 | it 'raise exit code to 1' do
24 | instance.raise
25 | expect(instance.instance_variable_get(:@exit_code)).to eq 1
26 | end
27 | end
28 |
29 | describe '#bucky_exit' do
30 | it 'the exit code should be 1' do
31 | instance.instance_variable_set(:@exit_code, 1)
32 | instance.bucky_exit
33 | rescue SystemExit => e
34 | expect(e.status).to eq(1)
35 | end
36 |
37 | it 'the exit code should be 0' do
38 | instance.instance_variable_set(:@exit_code, 0)
39 | instance.bucky_exit
40 | rescue SystemExit => e
41 | expect(e.status).to eq(0)
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/spec/utils/config_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../lib/bucky/utils/config'
4 |
5 | describe Bucky::Utils::Config do
6 | let(:config) { Bucky::Utils::Config.instance }
7 | let(:ssh_ip) { '123.123.123.123' }
8 |
9 | before do
10 | Singleton.__init__(Bucky::Utils::Config)
11 | Bucky::Utils::Config.class_variable_set(:@@dir, "#{__dir__}/*yml")
12 | allow_any_instance_of(described_class).to receive(:`).and_return(ssh_ip)
13 | end
14 |
15 | describe '#initialize' do
16 | it 'no error, when load yaml' do
17 | expect { config }.not_to raise_error
18 | end
19 | end
20 |
21 | describe '#switch_specified_word' do
22 | context 'when selenium_ip is "docker_host_ip"' do
23 | it '@data[:selenium_ip] is host server ip' do
24 | expect(config[:selenium_ip]).to eq(ssh_ip)
25 | end
26 | end
27 | end
28 |
29 | describe '#[]' do
30 | it 'no error, get data from multiple layers' do
31 | expect { config[:test_db][:bucky_test] }.not_to raise_error
32 | end
33 | it 'raise exception, when there is no specified key' do
34 | not_found_key = :whoamai
35 | expect { config[not_found_key] }.to raise_error("Undefined Config : #{not_found_key}\nKey doesn't match in config file. Please check config file in config/*")
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/utils/requests_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../lib/bucky/utils/requests'
4 |
5 | describe Bucky::Utils::Requests do
6 | let(:test_class) { Struct.new(:test_class) { include Bucky::Utils::Requests } }
7 | subject { test_class.new }
8 | let(:uri) { 'http://example.com' }
9 | let(:device) { 'pc' }
10 | let(:open_timeout) { 60 }
11 | let(:read_timeout) { 60 }
12 | let(:http) { Net::HTTP.new('test') }
13 |
14 | describe '#get_response' do
15 | context 'Valid URL is given' do
16 | let(:uri) { 'http://example.com' }
17 | it 'call Net::HTTP.get' do
18 | allow(Net::HTTP).to receive(:start).and_yield(http)
19 | Net::HTTP.start do |http|
20 | expect(http).to receive(:get)
21 | end
22 | subject.get_response(uri, device, open_timeout, read_timeout)
23 | end
24 | end
25 | context 'Unusual URI is given' do
26 | ['https://example.com/path/query[]=1/', 'http://例.com?query=[]'].each do |uri|
27 | it "#{uri} is given, call Net::HTTP.get" do
28 | allow(Net::HTTP).to receive(:start).and_yield(http)
29 | Net::HTTP.start do |http|
30 | expect(http).to receive(:get)
31 | end
32 | subject.get_response(uri, device, open_timeout, read_timeout)
33 | end
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/bucky/core/exception/bucky_exception.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../utils/bucky_logger'
4 | require_relative '../../utils/config'
5 |
6 | module Bucky
7 | module Core
8 | module Exception
9 | class BuckyException
10 | include Bucky::Utils::BuckyLogger
11 | class << self
12 | # Error handling on bucky framework
13 | # @param [Object] err exception object
14 | def handle(err)
15 | Bucky::Utils::BuckyLogger.write(Bucky::Utils::Config.instance[:bucky_error], err)
16 | end
17 | end
18 | end
19 |
20 | class DbConnectorException < Bucky::Core::Exception::BuckyException
21 | class << self
22 | def handle(err)
23 | super
24 | raise err
25 | end
26 | end
27 | end
28 |
29 | # Error handling on webdriver
30 | # @param [Object] err exception object
31 | # @param [String] proc_name
32 | class WebdriverException < Bucky::Core::Exception::BuckyException
33 | class << self
34 | def handle(err, proc_name = nil)
35 | super(err)
36 | raise err if proc_name.nil?
37 |
38 | raise(err.class, "#{err.message}\nFail in proc: ##{proc_name}")
39 | end
40 | end
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/example/hands-on/services/bucky_hands_on/pc/scenarios/e2e/search_and_assert.yml:
--------------------------------------------------------------------------------
1 | # Describe for this test suite
2 | desc: search in github and check page transition
3 | device: pc
4 | service: bucky_hands_on
5 | priority: high
6 | test_category: e2e
7 | labels:
8 | - example
9 | cases:
10 | # You should create test case name as {test suite name + _ + number}
11 | - case_name: search_and_assert_1
12 | func: transition
13 | desc: Should be able to search bucky-core in github, and move to bucky-core page.
14 | # Procedures to do in this case
15 | procs:
16 | - proc: Open github top page
17 | exec:
18 | operate: go
19 | url: https://github.com/
20 | - proc: Input 'bucky-core' in search bar
21 | exec:
22 | operate: input
23 | page: github_top
24 | part: search_bar
25 | word: 'bucky-core'
26 | - proc: Click search result
27 | exec:
28 | operate: click
29 | page: github_top
30 | part: search_resault
31 | - proc: Click target result
32 | exec:
33 | operate: click
34 | page: github_search_list
35 | part: bucky_core_a_tag
36 | - proc: assert_text
37 | exec:
38 | verify: assert_title
39 | expect: 'GitHub - lifull-dev/bucky-core: System testing framework for web application.'
40 |
--------------------------------------------------------------------------------
/example/bucky-management/services/bucky_example/pc/scenarios/e2e/search_and_assert.yml:
--------------------------------------------------------------------------------
1 | # Describe for this test suite
2 | desc: search in github and check page transition
3 | device: pc
4 | service: bucky_example
5 | priority: high
6 | test_category: e2e
7 | labels:
8 | - example
9 | cases:
10 | # You should create test case name as {test suite name + _ + number}
11 | - case_name: search_and_assert_1
12 | func: transition
13 | desc: Should be able to search bucky-core in github, and move to bucky-core page.
14 | # Procedures to do in this case
15 | procs:
16 | - proc: Open github top page
17 | exec:
18 | operate: go
19 | url: https://github.com/
20 | - proc: Input 'bucky-core' in search bar
21 | exec:
22 | operate: input
23 | page: github_top
24 | part: search_bar
25 | word: 'bucky-core'
26 | - proc: Click search result
27 | exec:
28 | operate: click
29 | page: github_top
30 | part: search_resault
31 | - proc: Click target result
32 | exec:
33 | operate: click
34 | page: github_search_list
35 | part: bucky_core_a_tag
36 | - proc: assert_text
37 | exec:
38 | verify: assert_title
39 | expect: 'GitHub - lifull-dev/bucky-core: System testing framework for web application.'
40 |
--------------------------------------------------------------------------------
/spec/utils/bucky_logger_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../lib/bucky/utils/bucky_logger'
4 | require 'test/unit'
5 | Test::Unit::AutoRunner.need_auto_run = false
6 |
7 | describe Bucky::Utils::BuckyLogger do
8 | describe '#write' do
9 | let(:klass) { Bucky::Utils::BuckyLogger }
10 | let(:file_name) { 'test' }
11 | let(:logger) { double('logger double') }
12 | subject { klass.write(file_name, content) }
13 | before do
14 | allow(Logger).to receive(:new).and_return(logger)
15 | end
16 |
17 | context 'when args is string object' do
18 | let(:content) { [:operation, 'go', :url, 'http://example.com/'].to_s }
19 | it 'call logger.info' do
20 | expect(logger).to receive(:info)
21 | allow(logger).to receive(:close)
22 | subject
23 | end
24 | it 'call logger.close' do
25 | allow(logger).to receive(:info)
26 | expect(logger).to receive(:close)
27 | subject
28 | end
29 | end
30 | context 'when args is error object' do
31 | let(:content) { Test::Unit::AssertionFailedError }
32 | it 'call logger.info' do
33 | expect(logger).to receive(:info)
34 | allow(logger).to receive(:close)
35 | subject
36 | end
37 | it 'call logger.close' do
38 | allow(logger).to receive(:info)
39 | expect(logger).to receive(:close)
40 | subject
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/system_testing/testing_code/command.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | BUCKY_PROJECT_NAME=bats_test
4 | TEST_SERVICE=test_service
5 |
6 | setup() {
7 | rm -rf /tmp/$BUCKY_PROJECT_NAME
8 | cd /tmp/
9 | bucky new $BUCKY_PROJECT_NAME
10 | }
11 |
12 | teardown() {
13 | rm -rf /tmp/$BUCKY_PROJECT_NAME
14 | }
15 |
16 | @test "[command] #1 After executing 'new' command, expected files and directories are created" {
17 | run diff /bucky-core/template/new/ /tmp/$BUCKY_PROJECT_NAME
18 | [ $status -eq 0 ]
19 | }
20 |
21 | @test "[command] #2 After executing 'make service' command, expected directory is created" {
22 | cd /tmp/$BUCKY_PROJECT_NAME
23 | bucky make service $TEST_SERVICE
24 | run ls /tmp/$BUCKY_PROJECT_NAME/services/$TEST_SERVICE
25 | [ $status -eq 0 ]
26 | }
27 |
28 | @test "[command] #3 After executing 'make page' command, expected page and parts file are created" {
29 | cd /tmp/$BUCKY_PROJECT_NAME
30 | bucky make service $TEST_SERVICE
31 | bucky make page test_page --service $TEST_SERVICE --device pc
32 | run ls /tmp/$BUCKY_PROJECT_NAME/services/$TEST_SERVICE/pc/pageobject/test_page.rb
33 | [ $status -eq 0 ]
34 | run ls /tmp/$BUCKY_PROJECT_NAME/services/$TEST_SERVICE/pc/parts/test_page.yml
35 | [ $status -eq 0 ]
36 | }
37 |
38 | @test "[command] #4 After executing undefined command, show error message and exit" {
39 | cd /tmp/$BUCKY_PROJECT_NAME
40 | run bucky hoge fuga
41 | [ $(expr "$output" : ".*Invalid command error.*") -ne 0 ]
42 | }
43 |
--------------------------------------------------------------------------------
/spec/core/test_core/test_case_loader_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/test_core/test_case_loader'
4 |
5 | describe Bucky::Core::TestCore::TestCaseLoader do
6 | describe '.load_testcode' do
7 | subject(:subject) { Bucky::Core::TestCore::TestCaseLoader.load_testcode(test_cond) }
8 | let(:bucky_home) { './spec/test_code' }
9 |
10 | before do
11 | $bucky_home_dir = bucky_home
12 | end
13 |
14 | context 'In case there are some arguments' do
15 | context 'when give args of test suite' do
16 | let(:test_cond) { { suite_name: [expect_scenario], test_category: 'e2e' } }
17 | let(:expect_scenario) { 'scenario_a' }
18 | it 'return test code object' do
19 | expect(subject).not_to be_empty
20 | end
21 | it 'return test code specified by test id' do
22 | subject.each do |code|
23 | expect(code[:test_suite_name]).to eq expect_scenario
24 | end
25 | end
26 | end
27 |
28 | context 'When give args of priority' do
29 | let(:test_cond) { { priority: [expect_priority], test_category: 'e2e' } }
30 | let(:expect_priority) { 'middle' }
31 | it 'return test code object' do
32 | expect(subject).not_to be_empty
33 | end
34 | it 'return test code specified by priority' do
35 | subject.each do |code|
36 | expect(code[:suite][:priority]).to eq expect_priority
37 | end
38 | end
39 | end
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/test_case/abst_test_case.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test/unit'
4 | require 'json'
5 | require_relative '../../core/test_core/test_result'
6 |
7 | module Bucky
8 | module TestEquipment
9 | module TestCase
10 | class AbstTestCase < Test::Unit::TestCase
11 | class << self
12 | def startup
13 | return if $debug
14 |
15 | @@this_result = Bucky::Core::TestCore::TestResult.instance
16 | @@added_result_info = {}
17 | end
18 |
19 | def shutdown
20 | @@this_result.save(@@added_result_info) unless $debug
21 | end
22 | end
23 |
24 | # Override Test::Unit::TestCase#run
25 | # Save test result to own test result object.
26 | def run(result)
27 | super
28 | @@this_result.result = result unless $debug
29 | w_pipe.puts({
30 | test_class_name: self.class.name,
31 | cases_count: result.run_count,
32 | success_count: result.pass_count,
33 | failure_count: result.run_count - result.pass_count
34 | }.to_json)
35 | end
36 |
37 | def setup
38 | # To make it easy to read
39 | puts "\n"
40 | end
41 |
42 | def teardown
43 | return if $debug
44 |
45 | @@added_result_info[method_name.to_sym] = {
46 | test_suite_id: suite_id,
47 | elapsed_time: Time.now - start_time,
48 | case_name: description
49 | }
50 | end
51 |
52 | def cleanup; end
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/bucky/utils/requests.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'net/http'
4 | require 'addressable/uri'
5 |
6 | module Bucky
7 | module Utils
8 | module Requests
9 | USER_AGENT_STRING = {
10 | pc: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36',
11 | sp: 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E238 Safari/601.1'
12 | }.freeze
13 |
14 | # @param [String] uri
15 | # @param [String] device
16 | # @param [Integer/Float] open_timeout max wait time until open page
17 | # @param [Integer/Float] read_timeout max wait time until recieve response
18 | # @return [Net::HTTP] HttpStatusCode
19 | def get_response(uri, device, open_timeout, read_timeout)
20 | parsed_uri = Addressable::URI.parse(uri.to_str.strip)
21 | query = if parsed_uri.query.nil?
22 | ''
23 | else
24 | '?' + parsed_uri.query_values.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v)}" }.join('&')
25 | end
26 | # If path is empty, add "/" e.g) http://example.com
27 | path = parsed_uri.path.empty? ? '/' : parsed_uri.path
28 |
29 | Net::HTTP.start(parsed_uri.host, parsed_uri.port, use_ssl: parsed_uri.scheme == 'https') do |http|
30 | http.open_timeout = open_timeout
31 | http.read_timeout = read_timeout
32 | http.get("#{path}#{query}", 'User-Agent' => USER_AGENT_STRING[device.to_sym])
33 | end
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/spec/core/exception/bucky_exception_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/exception/bucky_exception'
4 |
5 | describe Bucky::Core::Exception do
6 | let(:error) { StandardError.new('error test') }
7 | let(:config_double) { double('double of Config') }
8 | before do
9 | allow(Bucky::Utils::Config).to receive(:instance).and_return(config_double)
10 | allow(config_double).to receive('[]').and_return(bucky_error: 'test')
11 | end
12 |
13 | describe Bucky::Core::Exception::BuckyException do
14 | let(:klass) { Bucky::Core::Exception::BuckyException }
15 | describe '.handle' do
16 | it 'call BuckyLogger.write' do
17 | expect(Bucky::Utils::BuckyLogger).to receive(:write)
18 | klass.handle(error)
19 | end
20 | end
21 | end
22 |
23 | describe Bucky::Core::Exception::DbConnectorException do
24 | let(:klass) { Bucky::Core::Exception::DbConnectorException }
25 | describe '.handle' do
26 | it 'raise error' do
27 | allow(Bucky::Core::Exception::BuckyException).to receive(:handle)
28 | expect { klass.handle(error) }.to raise_error(error)
29 | end
30 | end
31 | end
32 |
33 | describe Bucky::Core::Exception::WebdriverException do
34 | let(:klass) { Bucky::Core::Exception::WebdriverException }
35 | describe '.handle' do
36 | it 'raise error' do
37 | allow(Bucky::Core::Exception::BuckyException).to receive(:handle)
38 | expect { klass.handle(error) }.to raise_error(error)
39 | end
40 |
41 | let(:proc_name) { '1:test proc' }
42 | it 'raise error with proc_name' do
43 | allow(Bucky::Core::Exception::BuckyException).to receive(:handle)
44 | expect { klass.handle(error, proc_name) }.to raise_error(StandardError, "error test\nFail in proc: #1:test proc")
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by
2 | # `rubocop --auto-gen-config`
3 | # on 2018-07-30 18:33:29 +0900 using RuboCop version 0.58.2.
4 | # The point is for the user to remove these configuration records
5 | # one by one as the offenses are removed from the code base.
6 | # Note that changes in the inspected code, or installation of new
7 | # versions of RuboCop, may require this file to be generated again.
8 |
9 | # Offense count: 1
10 | # Configuration parameters: CountComments.
11 | Metrics/ClassLength:
12 | Max: 105
13 | Exclude:
14 | - 'lib/bucky/core/database/test_data_operator.rb'
15 |
16 | # Offense count: 3
17 | Security/Eval:
18 | Exclude:
19 | - 'lib/bucky/core/test_core/test_class_generator.rb'
20 | - 'lib/bucky/test_equipment/pageobject/pages.rb'
21 | - 'lib/bucky/test_equipment/verifications/service_verifications.rb'
22 |
23 | # Offense count: 2
24 | # Configuration parameters: EnforcedStyle.
25 | # SupportedStyles: inline, group
26 | Style/AccessModifierDeclarations:
27 | Exclude:
28 | - 'lib/bucky/test_equipment/selenium_handler/webdriver_handler.rb'
29 | - 'lib/bucky/utils/bucky_logger.rb'
30 |
31 | # Offense count: 2
32 | Lint/MissingSuper:
33 | Exclude:
34 | - 'lib/bucky/test_equipment/user_operation/user_operator.rb'
35 | - 'lib/bucky/test_equipment/verifications/service_verifications.rb'
36 |
37 | # Offense count: 2
38 | Style/MissingRespondToMissing:
39 | Exclude:
40 | - 'lib/bucky/test_equipment/user_operation/user_operator.rb'
41 | - 'lib/bucky/test_equipment/verifications/service_verifications.rb'
42 |
43 | # Offense count: 229
44 | # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
45 | # URISchemes: http, https
46 | Layout/LineLength:
47 | Max: 225
48 |
49 | Style/ExpandPathArguments:
50 | Exclude:
51 | - 'bucky-core.gemspec' # for lifull gem
52 |
--------------------------------------------------------------------------------
/lib/bucky/utils/config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'singleton'
4 | require_relative './yaml_load'
5 |
6 | module Bucky
7 | module Utils
8 | class Config
9 | include Bucky::Utils::YamlLoad
10 | include Singleton
11 | @@dir = "#{$bucky_home_dir}/config/**/*yml"
12 |
13 | attr_reader :data
14 |
15 | # @param [String] *.yml or hoge/fuga.yml
16 | def initialize
17 | @data = {}
18 | @resources = []
19 | @default_config_dir = File.expand_path('../../../template/new/config', __dir__)
20 | # Read from a file of shallow hierarchy, then overwrite it if there is same key in deep hierarchy
21 | file_sort_hierarchy(@@dir).each do |file|
22 | file_name = file.split('/').last
23 | default_config_file = "#{@default_config_dir}/#{file_name}"
24 | data = load_yaml(file)
25 | next if data.empty?
26 |
27 | if File.exist?(default_config_file)
28 | default_config_data = load_yaml(default_config_file)
29 | data = default_config_data.merge(data)
30 | end
31 | @data = @data.merge(data)
32 | @resources << file
33 | end
34 |
35 | set_selenium_ip
36 | end
37 |
38 | # Get data by []
39 | def [](column)
40 | return @data[column] if @data.key?(column)
41 |
42 | # If there is no key, raise exeption
43 | raise "Undefined Config : #{column}\nKey doesn't match in config file. Please check config file in config/*"
44 | end
45 |
46 | private
47 |
48 | def set_selenium_ip
49 | return unless @data[:selenium_ip] == 'docker_host_ip'
50 |
51 | selenium_ip = `ip route | awk 'NR==1 {print $3}'`.chomp
52 | raise StandardError, 'Could not load docker host ip.' if selenium_ip.empty?
53 |
54 | @data[:selenium_ip] = selenium_ip
55 | end
56 | end
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: .rubocop_todo.yml
2 |
3 | AllCops:
4 | NewCops: disable
5 | Exclude:
6 | - 'lib/bucky/test_equipment/user_operation/user_operation_helper.rb'
7 | - 'spec/fixtures/**/*'
8 | - 'spec/test_code/**/*'
9 | - 'template/**/*'
10 | - 'tmp/**/*'
11 | - 'vendor/**/*'
12 | TargetRubyVersion: 3.2
13 |
14 | Metrics/AbcSize:
15 | Enabled: false
16 |
17 | # Allow method prefix start with 'set_' and 'get_'
18 | Naming/AccessorMethodName:
19 | Enabled: false
20 |
21 | Metrics/BlockLength:
22 | Exclude:
23 | - 'Rakefile'
24 | - '**/*.rake'
25 | - 'spec/**/*.rb'
26 | - 'bucky-core.gemspec'
27 | - 'lib/bucky/core/database/test_data_operator.rb'
28 | - 'lib/bucky/core/test_core/test_class_generator.rb'
29 |
30 | Style/ClassVars:
31 | Exclude:
32 | - 'lib/bucky/test_equipment/selenium_handler/webdriver_handler.rb'
33 | - 'lib/bucky/test_equipment/test_case/abst_test_case.rb'
34 | - 'lib/bucky/utils/config.rb'
35 | - 'lib/bucky/tools/lint.rb'
36 |
37 | Metrics/CyclomaticComplexity:
38 | Enabled: false
39 |
40 | # Allow missing top-level documentation comment of class
41 | Style/Documentation:
42 | Enabled: false
43 |
44 | Style/FrozenStringLiteralComment:
45 | EnforcedStyle: always
46 |
47 | Style/GlobalVars:
48 | AllowedVariables: [$bucky_home_dir, $debug, $job_id, $round]
49 |
50 | # Not allow using '->' for multi-line lambdas.
51 | Style/Lambda:
52 | Enabled: false
53 |
54 | Layout/LineLength:
55 | Enabled: false
56 |
57 | Lint/AmbiguousBlockAssociation:
58 | Exclude:
59 | - 'spec/**/*.rb'
60 |
61 | Metrics/MethodLength:
62 | Enabled: false
63 |
64 | Metrics/ModuleLength:
65 | Max: 140
66 | Exclude:
67 | - 'spec/**/*.rb'
68 |
69 | Metrics/PerceivedComplexity:
70 | Enabled: false
71 |
72 | Lint/UselessAssignment:
73 | Exclude:
74 | - 'lib/bucky/core/test_core/test_result.rb'
75 |
76 | Metrics/ClassLength:
77 | Max: 110
78 |
79 | Lint/UselessAccessModifier:
80 | Exclude:
81 | - 'example/**/*'
82 |
--------------------------------------------------------------------------------
/example/bucky-management/README.md:
--------------------------------------------------------------------------------
1 | # Bucky-management
2 |
3 | ## What is this example doing
4 | This example will show you how to connect to Bucky-management and how to get test report automatically.
5 |
6 | ## Before starting
7 | Start Bucky-management with docker-compose.yml.
8 | See [READEME](https://github.com/lifull-dev/bucky-management) in Bucky-management if you start it at first time.
9 | ```bash
10 | # Execute this command in Bucky-management directory
11 | docker-compose -f docker-compose.yml up --build -d
12 | ```
13 | ## 1. Move to you test project directory
14 | - You can see how to make test project in [example/hands-on](https://github.com/lifull-dev/bucky-core/tree/master/example/hands-on)
15 | ```bash
16 | # In this example, you can just stay in example/bucky-management
17 | cd {test project name}
18 | ```
19 |
20 | ## 2. Sets connect information to DB
21 | - Use default value as following if you didn't change anything in Bucky-management's docker-compose.yml
22 | - Database name is according to what you set in RAILS_ENV when startup Bucky-management
23 | ```
24 | ## config/test_db_config.yml ##
25 |
26 | :test_db:
27 | :bucky_test:
28 | :username: root
29 | :password: password
30 | :database: bucky_test # When $RAILS_ENV=test
31 | :host: 127.0.0.1
32 | :port: 3306
33 | :adapter: :mysql
34 | ```
35 |
36 | ## 3. Execute test case without -d option
37 | It will save test result into DB executing without debug option.
38 | ```bash
39 | # These test case is already made. You can just execute them in example/bucky-management
40 | bucky run -t e2e -D pc -c search_and_assert_1
41 | bucky run -t linkstatus -D pc -c github_top_1
42 | ```
43 |
44 | ## 4. Check your test report
45 | Open your browser http://localhost
46 |
47 | You will see two result shown in top page.
48 |
49 | ## At last
50 | Congratulations!! You have just made your test report.
51 |
52 | There are lots of information shown in reports.
53 | It shows more information in each report's detail, e.g. pass rate, NG cases name, NG error message and so on.
54 |
55 | Enjoy your test with Bucky!!
56 |
--------------------------------------------------------------------------------
/system_testing/testing_code/linkstatus.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | setup() {
4 | cd /bucky-core/system_testing/test_bucky_project
5 | }
6 |
7 | @test "[linkstatus] #1, #3 After executing linkstatus on pc, results contain target base url, no failures/errors and exit code is 0." {
8 | run bucky run -t linkstatus -d -D pc -c pc_link_1
9 | [ $status -eq 0 ]
10 | [ $(expr "$output" : ".*http://bucky\.net.*") -ne 0 ]
11 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
12 | }
13 |
14 | @test "[linkstatus] #2 After executing linkstatus on pc, results contain target link url and no failures/errors." {
15 | run bucky run -t linkstatus -d -D pc -c pc_link_1
16 | [ $(expr "$output" : ".*http://bucky\.net/test_page.html.*") -ne 0 ]
17 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
18 | }
19 |
20 | @test "[linkstatus] #4 After executing linkstatus failed test, exit code is 1" {
21 | run bucky run -t linkstatus -d -D pc -c pc_link_2
22 | [ $status -eq 1 ]
23 | }
24 |
25 | @test "[linkstatus] #5 After executing linkstatus on sp, results have no failures nor errors" {
26 | run bucky run -t linkstatus -d -D sp -c sp_link_1
27 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
28 | }
29 |
30 | @test "[linkstatus] #6 After executing linkstatus with excluding by normal, results not contain target link url and no failures/errors." {
31 | run bucky run -t linkstatus -d -D pc -c pc_link_exclude_normal_1
32 | [ $status -eq 0 ]
33 | [ $(expr "$output" : ".*http://bucky\.net/test_page.html.*") -eq 0 ]
34 | }
35 |
36 | @test "[linkstatus] #7 After executing linkstatus with excluding by asterisk, results not contain target link url and no failures/errors." {
37 | run bucky run -t linkstatus -d -D pc -c pc_link_exclude_asterisk_1
38 | [ $status -eq 0 ]
39 | [ $(expr "$output" : ".*http://bucky\.net/test_page.html.*") -eq 0 ]
40 | }
41 |
42 | @test "[linkstatus] #8 After executing linkstatus with excluding by regex, results not contain target link url and no failures/errors." {
43 | run bucky run -t linkstatus -d -D pc -c pc_link_exclude_regex_1
44 | [ $status -eq 0 ]
45 | [ $(expr "$output" : ".*http://bucky\.net/test_page.html.*") -eq 0 ]
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/pageobject/pages.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Bucky
4 | module TestEquipment
5 | module PageObject
6 | class Pages
7 | def initialize(service, device, driver)
8 | collect_pageobjects(service, device, driver)
9 | end
10 |
11 | # Load page class and define page method
12 | # @param [String] service
13 | # @param [String] device (pc, sp)
14 | # @param [Object] driver Webdriver object
15 | def collect_pageobjects(service, device, driver)
16 | # Create module name
17 | module_service_name = service.split('_').map(&:capitalize).join
18 | Dir.glob("#{$bucky_home_dir}/services/#{service}/#{device}/pageobject/*.rb").each do |file|
19 | require file
20 |
21 | page_name = file.split('/')[-1].sub('.rb', '')
22 | page_class_name = page_name.split('_').map(&:capitalize).join
23 |
24 | # Get instance of page class
25 | page_class = eval(format('Services::%s::%s::PageObject::%s', module_service_name: module_service_name, device: device.capitalize, page_class_name: page_class_name))
26 | page_instance = page_class.new(service, device, page_name, driver)
27 |
28 | # Define method by page name
29 | self.class.class_eval do
30 | define_method(page_name) { page_instance }
31 | end
32 | end
33 | end
34 |
35 | # Get Web element by page, part, num
36 | # @param [Hash] args
37 | def get_part(args)
38 | return send(args[:page]).send(args[:part][:locate])[args[:part][:num]] if part_plural?(args)
39 |
40 | send(args[:page]).send(args[:part])
41 | end
42 |
43 | # @param [Hash] args
44 | # @return [Boolean]
45 | def part_plural?(args)
46 | # If the part is hash and has 'num' key, it has plural elements.
47 | args[:part].class == Hash && args[:part].key?(:num)
48 | end
49 |
50 | # @param [Hash] args
51 | # @return [Bool]
52 | def part_exist?(args)
53 | get_part(args)
54 | true
55 | rescue Selenium::WebDriver::Error::NoSuchElementError
56 | false
57 | end
58 | end
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/lib/bucky/tools/lint.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../utils/yaml_load'
4 |
5 | module Bucky
6 | module Tools
7 | class Lint
8 | class << self
9 | include Bucky::Utils::YamlLoad
10 | @@config_dir = "#{$bucky_home_dir}/config/**/*yml"
11 | @@rule_config_dir = File.expand_path('../../../template/new/config', __dir__) + '/*yml'
12 |
13 | def check(category)
14 | method = "check_#{category}".to_sym
15 | respond_to?(method) ? send(method) : raise(StandardError, "no such a category #{category}")
16 | end
17 |
18 | # If you want to add new category, please make new method
19 | def check_config
20 | data = merge_yaml_data(@@config_dir)
21 | @rule_data = merge_yaml_data(@@rule_config_dir)
22 | actual = make_key_chain(data)
23 | expect = make_key_chain(@rule_data)
24 | diff = diff_arr(expect, actual)
25 | make_message(diff)
26 | end
27 |
28 | private
29 |
30 | # Merge yaml in target directory.
31 | def merge_yaml_data(dir)
32 | data_output = {}
33 | file_sort_hierarchy(dir).each do |file|
34 | data = load_yaml(file)
35 | data_output.merge!(data) unless data.empty?
36 | end
37 | data_output.any? ? data_output : (raise StandardError, "No key! please check the directory existence [#{dir}]")
38 | end
39 |
40 | def make_message(diff)
41 | if diff.empty?
42 | puts "\e[32mok\e[0m"
43 | else
44 | puts "\e[31m[ERROR] The following configures are undefined. Tests can still be executed with default value automatically."
45 | diff.each do |key|
46 | puts "- #{key}"
47 | puts "{#{key}: #{@rule_data[:"#{key}"]}}\e[0m"
48 | end
49 | end
50 | end
51 |
52 | def diff_arr(expect, actual)
53 | expect.delete_if do |i|
54 | actual.include?(i)
55 | end
56 | end
57 |
58 | def make_key_chain(hash)
59 | hash.map do |k, v|
60 | if v.is_a? Hash
61 | make_key_chain(v).map { |item| "#{k}-#{item}" }
62 | else
63 | k.to_s
64 | end
65 | end.flatten
66 | end
67 | end
68 | end
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/user_operation/user_operator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../core/exception/bucky_exception'
4 | require_relative './user_operation_helper'
5 | require_relative '../../utils/bucky_logger'
6 |
7 | module Bucky
8 | module TestEquipment
9 | module UserOperation
10 | class UserOperator
11 | include Bucky::Utils::BuckyLogger
12 |
13 | def initialize(args)
14 | @operation_helper = Bucky::TestEquipment::UserOperation::UserOperationHelper.new(args)
15 | @pages = args[:pages]
16 | end
17 |
18 | # Call user operation by argument
19 | # @param [String] operation
20 | # @param [String] test_case_name
21 | # @param [Hash] args
22 | def method_missing(operation, test_case_name, **args)
23 | @operation = operation
24 | @test_case_name = test_case_name
25 | Bucky::Utils::BuckyLogger.write(test_case_name, args[:exec])
26 |
27 | # Call method of UserOperationHelper
28 | return @operation_helper.send(@operation, args[:exec]) if @operation_helper.methods.include?(@operation)
29 |
30 | # Call method of page object
31 | # e.g) {page: 'top', operation: 'input_freeword', word: 'testing word'}
32 | return page_method(args[:exec]) if args[:exec].key?(:page) && !args[:exec].key?(:part)
33 |
34 | # Call method of part
35 | part_mothod(args[:exec]) if args[:exec].key?(:part)
36 | rescue StandardError => e
37 | Bucky::Core::Exception::WebdriverException.handle(e, "#{args[:step_number]}:#{args[:proc_name]}")
38 | end
39 |
40 | private
41 |
42 | def page_method(args)
43 | @pages.send(args[:page]).send(@operation, args)
44 | end
45 |
46 | def part_mothod(args)
47 | # Multiple parts is saved as hash
48 | # e.g){page: 'top', part: {locate: 'rosen_tokyo', num: 1}, operate: 'click'}
49 | if args[:part].class == Hash
50 | part_name = args[:part][:locate]
51 | num = args[:part][:num]
52 | @pages.send(args[:page]).send(part_name)[num].send(@operation)
53 | # e.g.){page: 'top', part: 'rosen_tokyo', operate: 'click'}
54 | else
55 | @pages.send(args[:page]).send(args[:part]).send(@operation)
56 | end
57 | end
58 | end
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/test_case/e2e_test_case.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'selenium-webdriver'
4 | require_relative './abst_test_case'
5 | require_relative '../user_operation/user_operator'
6 | require_relative '../pageobject/pages'
7 | require_relative '../selenium_handler/webdriver_handler'
8 | require_relative '../verifications/service_verifications'
9 |
10 | module Bucky
11 | module TestEquipment
12 | module TestCase
13 | class E2eTestCase < Bucky::TestEquipment::TestCase::AbstTestCase
14 | include Bucky::TestEquipment::SeleniumHandler::WebdriverHandler
15 |
16 | TEST_CATEGORY = 'e2e'
17 |
18 | class << self
19 | def startup; end
20 |
21 | def shutdown; end
22 | end
23 |
24 | # Initialize the following class
25 | # - webdriver
26 | # - page object
27 | # - user oparation
28 | # - verification
29 | # @param [Hash] suite
30 | def t_equip_setup
31 | @driver = create_webdriver(suite_data[:device])
32 | @pages = Bucky::TestEquipment::PageObject::Pages.new(suite_data[:service], suite_data[:device], @driver)
33 | service_verifications_args = { service: suite_data[:service], device: suite_data[:device], driver: @driver, pages: @pages, method_name: }
34 | @service_verifications = Bucky::TestEquipment::Verifications::ServiceVerifications.new(service_verifications_args)
35 | user_operator_args = { app: suite_data[:service], device: suite_data[:device], driver: @driver, pages: @pages }
36 | @user_operator = Bucky::TestEquipment::UserOperation::UserOperator.new(user_operator_args)
37 | end
38 |
39 | # Call mothod of verification
40 | # @param [Hash] verify_args e.g.) {:exec=>{verify: "assert_title", expect: "page title"}, :step_number=> 1, :proc_name=> "test proc"}
41 | def verify(**verify_args)
42 | @service_verifications.send(verify_args[:exec][:verify], **verify_args)
43 | end
44 |
45 | # Call method of user operation
46 | # @param [Hash] op_args e.g.) {:exec=>{:operate=>"click", :page=>"top_page", :part=>"fizz_button"}, :step_number=> 1, :proc_name=> "test proc"}
47 | def operate(**op_args)
48 | @user_operator.send(op_args[:exec][:operate], method_name, **op_args)
49 | end
50 |
51 | def setup
52 | super
53 | t_equip_setup
54 | end
55 |
56 | def teardown
57 | @driver.quit
58 | ensure
59 | super
60 | end
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/verifications/service_verifications.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../verifications/e2e_verification'
4 |
5 | module Bucky
6 | module TestEquipment
7 | module Verifications
8 | class ServiceVerifications
9 | attr_reader :e2e_verification
10 |
11 | # @param [String] @service
12 | # @param [String] @device (pc, sp)
13 | # @param [Selenium::WebDriver::Remote::Driver] @driver
14 | # @param [Bucky::TestEquipment::PageObject::Pages] @pages
15 | # @param [String] @test_case_name
16 | def initialize(args)
17 | @service = args[:service]
18 | @device = args[:device]
19 | @driver = args[:driver]
20 | @pages = args[:pages]
21 | @test_case_name = args[:method_name]
22 | collect_verifications
23 | @e2e_verification = Bucky::TestEquipment::Verifications::E2eVerification.new(@driver, @pages, @test_case_name)
24 | end
25 |
26 | def method_missing(verification, **args)
27 | if e2e_verification.respond_to? verification
28 | puts " #{verification} is defined in E2eVerificationClass."
29 | e2e_verification.send(verification, **args[:exec])
30 | elsif args[:exec].key?(:page)
31 | send(args[:exec][:page]).send(verification, **args[:exec])
32 | else
33 | raise StandardError, "Undefined verification method or invalid arguments. #{verification},#{args[:exec]}"
34 | end
35 | rescue StandardError => e
36 | Bucky::Core::Exception::WebdriverException.handle(e, "#{args[:step_number]}:#{args[:proc_name]}")
37 | end
38 |
39 | private
40 |
41 | # Load page and define page verification method
42 | def collect_verifications
43 | module_service_name = @service.split('_').map(&:capitalize).join
44 | Dir.glob("#{$bucky_home_dir}/services/#{@service}/#{@device}/verifications/*.rb").each do |file|
45 | require file
46 |
47 | page_name = file.split('/')[-1].sub('.rb', '')
48 | page_class_name = page_name.split('_').map(&:capitalize).join
49 |
50 | # Get instance of page object
51 | page_class = eval(format('Services::%s::%s::Verifications::%s', module_service_name:, device: @device.capitalize, page_class_name:))
52 | page_instance = page_class.new(@driver, @pages, @test_case_name)
53 |
54 | self.class.class_eval do
55 | define_method(page_name) { page_instance }
56 | end
57 | end
58 | end
59 | end
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/spec/test_equipment/pageobject/pages_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/pageobject/pages'
4 | require 'selenium-webdriver'
5 |
6 | describe Bucky::TestEquipment::PageObject::Pages do
7 | let(:service) { 'service_a' }
8 | let(:device) { 'pc' }
9 | let(:driver) { 'driver' }
10 | let(:page_name) { :sample_page }
11 | let(:bucky_home) { './spec/test_code' }
12 |
13 | before { $bucky_home_dir = bucky_home }
14 | subject { Bucky::TestEquipment::PageObject::Pages.new(service, device, driver) }
15 |
16 | describe '#initialize' do
17 | it 'define instance method with pageobject name' do
18 | expect(subject).to respond_to page_name
19 | end
20 | end
21 |
22 | describe '#get_part' do
23 | let(:page_double) { double('page double') }
24 | let(:part_double) { double('part double') }
25 | context 'in case single part' do
26 | let(:operation_args) { { page: 'test_page', part: 'rosen_tokyo' } }
27 | it 'call send on partobject' do
28 | allow(subject).to receive(:send).and_return(page_double)
29 | expect(page_double).to receive(:send).with(operation_args[:part])
30 | subject.get_part(operation_args)
31 | end
32 | end
33 | context 'in case operate one part of multiple parts' do
34 | let(:operation_args) { { page: 'top', part: { locate: 'rosen_tokyo', num: 1 } } }
35 | let(:parts_double) { double('parts double') }
36 | it 'call [] on partobject' do
37 | allow(subject).to receive(:send).and_return(parts_double)
38 | allow(parts_double).to receive(:send).and_return(part_double)
39 | expect(part_double).to receive(:[])
40 | subject.get_part(operation_args)
41 | end
42 | end
43 | end
44 |
45 | describe '#part_plural?' do
46 | let(:args) { { page: 'bukken_detail', part: { locate: 'buttons', num: 0 } } }
47 | it 'if part have "num", return true' do
48 | expect(subject.part_plural?(args)).to be true
49 | end
50 | end
51 |
52 | describe '#part_exist?' do
53 | let(:get_part_double) { double('get_part double') }
54 | let(:operation_args) { { page: 'test_page', part: 'rosen_tokyo' } }
55 | let(:error) { Selenium::WebDriver::Error::NoSuchElementError }
56 | context 'get part' do
57 | it 'if part exists, return true' do
58 | allow(subject).to receive(:get_part).and_return(get_part_double)
59 | expect(subject.part_exist?(operation_args)).to be true
60 | end
61 | it 'if raise NoSuchElementError, return false' do
62 | allow(subject).to receive(:get_part).and_raise(error)
63 | expect(subject.part_exist?(operation_args)).to be false
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/bucky-core.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | lib = File.expand_path('../lib', __FILE__)
4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5 | require 'bucky/version'
6 |
7 | Gem::Specification.new do |spec|
8 | spec.name = 'bucky-core'
9 | spec.version = Bucky::Version::VERSION
10 | spec.authors = %w[NaotoKishino HikaruFukuzawa SeiyaSato NaokiNakano RikiyaHikimochi JyeRuey]
11 | spec.email = ['KishinoNaoto@lifull.com']
12 |
13 | spec.summary = 'System testing framework for web application.'
14 | spec.description = <<-DESCRIPTION
15 | Bucky-core can run test code which is written in YAML. End-to-End test (working with Selenium) and Linkstatus test (HTTP status check) are supported in default. Page object model pattern and page based element management is the main concept in Bucky-core. You can create scenarios and execute it easily by using Bucky-core.
16 |
17 | When working with Bucky-management, Bucky-core can also record test results. You can make test results visualization by using Bucky-management.
18 | DESCRIPTION
19 | spec.required_ruby_version = '>= 2.5'
20 | spec.homepage = 'https://github.com/lifull-dev/bucky-core'
21 | spec.license = 'Apache License 2.0'
22 |
23 | spec.files = `git ls-files -z`.split("\x0").reject do |f|
24 | f.match(%r{^(test|spec|features)/})
25 | end
26 | spec.bindir = 'exe'
27 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28 | spec.require_paths = ['lib']
29 |
30 | spec.add_development_dependency 'awesome_print', '~> 1.8'
31 | spec.add_development_dependency 'bundler', '2.5.18'
32 | spec.add_development_dependency 'hirb', '~> 0.7'
33 | spec.add_development_dependency 'pry', '~> 0.10'
34 | spec.add_development_dependency 'pry-byebug', '~> 3.4'
35 | spec.add_development_dependency 'pry-stack_explorer', '~> 0.4'
36 | spec.add_development_dependency 'rake', '~> 13'
37 | spec.add_development_dependency 'rspec', '~> 3.6'
38 | spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
39 | spec.add_development_dependency 'rubocop', '1.66.1'
40 | spec.add_development_dependency 'simplecov', '~> 0.15.1'
41 | spec.add_development_dependency 'simplecov-console', '~> 0.4.2'
42 |
43 | spec.add_runtime_dependency 'addressable', '~> 2.5'
44 | spec.add_runtime_dependency 'color_echo', '~> 3.1'
45 | spec.add_runtime_dependency 'json', '~> 2.3.0'
46 | spec.add_runtime_dependency 'nokogiri', '1.18.2'
47 | spec.add_runtime_dependency 'parallel', '~> 1.11'
48 | spec.add_runtime_dependency 'ruby-mysql', '~> 2.9'
49 | spec.add_runtime_dependency 'selenium-webdriver', '4.24'
50 | spec.add_runtime_dependency 'sequel', '5.84'
51 | spec.add_runtime_dependency 'test-unit', '~> 3.2'
52 | end
53 |
--------------------------------------------------------------------------------
/spec/test_equipment/verifications/service_verifications_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/verifications/service_verifications'
4 |
5 | describe Bucky::TestEquipment::Verifications::ServiceVerifications do
6 | let(:pages) { double('page') }
7 | let(:e2e_verification) { double('e2e_verification') }
8 | let(:assert_title_mock) { double('assert_title_mock') }
9 | let(:page_name) { :sample_page }
10 | let(:args) { { service: 'service_a', device: 'pc', driver: 'driver', method_name: 'test_case_name', pages: } }
11 | let(:bucky_home) { './spec/test_code' }
12 |
13 | subject { Bucky::TestEquipment::Verifications::ServiceVerifications.new(args) }
14 | before do
15 | $bucky_home_dir = bucky_home
16 | allow(pages).to receive(:send).and_return(page_name)
17 | end
18 |
19 | describe '#initialize' do
20 | it 'define all instance valiables' do
21 | expect(subject.instance_variable_get(:@service)).to eq 'service_a'
22 | expect(subject.instance_variable_get(:@device)).to eq 'pc'
23 | expect(subject.instance_variable_get(:@driver)).to eq 'driver'
24 | expect(subject.instance_variable_get(:@test_case_name)).to eq 'test_case_name'
25 | expect(subject.instance_variable_get(:@pages)).to eq pages
26 | end
27 | end
28 |
29 | describe '#method_missing' do
30 | let(:verify_args) { { exec: { verify: 'assert_title', expect: 'page title' }, step_number: 1, proc_name: 'test proc' } }
31 | let(:verify_page_args) { { exec: { page: page_name, verify: 'assert_sample', expect: 'page title' }, step_number: 1, proc_name: 'test proc' } }
32 | let(:dummy_verify_args) { { exec: { verify: 'hoge', expect: 'hoge' }, step_number: 1, proc_name: 'test proc' } }
33 | let(:page_method_double) { double('page method') }
34 | before do
35 | allow(Bucky::TestEquipment::Verifications::E2eVerification).to receive(:new).and_return(e2e_verification)
36 | end
37 | it 'if common e2e method, call send to e2e_verification' do
38 | allow(e2e_verification).to receive(:respond_to?).and_return(true)
39 | expect(e2e_verification).to receive(:send)
40 | subject.send('assert_title', **verify_args)
41 | end
42 | it 'if page verify method, call method of page instance' do
43 | allow(e2e_verification).to receive(:respond_to?).and_return(false)
44 | allow(args).to receive(:key?).and_return(true)
45 | allow(subject).to receive(page_name).and_return(page_method_double)
46 | expect(page_method_double).to receive(:assert_sample)
47 | subject.send('assert_sample', **verify_page_args)
48 | end
49 | let(:args_mock) { double('args_mock') }
50 | it 'if call undefined method, raise exception' do
51 | allow(e2e_verification).to receive(:respond_to?).and_return(false)
52 | allow(args).to receive(:key?).and_return(false)
53 | expect { subject.send('undefined method', dummy_verify_args) }.to raise_error(StandardError)
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/selenium_handler/webdriver_handler.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'selenium-webdriver'
4 | require_relative '../../core/exception/bucky_exception'
5 | require_relative '../../utils/config'
6 |
7 | module Bucky
8 | module TestEquipment
9 | module SeleniumHandler
10 | module WebdriverHandler
11 | # Create and return webdriver object
12 | # @param [String] device_type e.g.) sp, pc, tablet
13 | # @return [Selenium::WebDriver]
14 | def create_webdriver(device_type)
15 | @@config = Bucky::Utils::Config.instance
16 | driver_args = create_driver_args(device_type)
17 | # Correctly create an options object
18 | options = generate_desire_caps(device_type)
19 | driver = Selenium::WebDriver.for :remote, url: driver_args[:url], options:, http_client: driver_args[:http_client]
20 | driver.manage.window.resize_to(1920, 1080)
21 | driver.manage.timeouts.implicit_wait = @@config[:find_element_timeout]
22 | driver
23 | rescue StandardError => e
24 | Bucky::Core::Exception::BuckyException.handle(e)
25 | end
26 | module_function :create_webdriver
27 |
28 | private
29 |
30 | # @param [String] device_type e.g.) sp, pc, tablet
31 | # @return [Hash] driver_args
32 | def create_driver_args(_device_type)
33 | {
34 | url: format('http://%s:%s/wd/hub', ip: @@config[:selenium_ip], port: @@config[:selenium_port]),
35 | http_client: create_http_client
36 | }
37 | end
38 |
39 | def create_http_client
40 | client = Selenium::WebDriver::Remote::Http::Default.new
41 | client.open_timeout = @@config[:driver_open_timeout]
42 | client.read_timeout = @@config[:driver_read_timeout]
43 | client
44 | end
45 |
46 | # Generate the desired capabilities
47 | # @param [String] device_type e.g.) sp, pc, tablet
48 | # @return [Selenium::WebDriver::Options]
49 | def generate_desire_caps(device_type)
50 | case @@config[:browser]
51 | when :chrome
52 | set_chrome_option(device_type)
53 | else
54 | raise 'Currently only supports chrome. Sorry.'
55 | end
56 | end
57 |
58 | def set_chrome_option(device_type)
59 | options = Selenium::WebDriver::Options.chrome
60 | if %w[sp tablet].include?(device_type)
61 | device_type = "#{device_type}_device_name".to_sym
62 | options.add_emulation(device_name: @@config[:device_name_on_chrome][@@config[device_type]])
63 | end
64 | options.add_argument("--user-agent=#{@@config[:user_agent]}") if @@config[:user_agent]
65 | options.add_argument('--headless') if @@config[:headless]
66 | @@config[:chromedriver_flags]&.each do |flag|
67 | options.add_argument(flag)
68 | end
69 | options
70 | end
71 | end
72 | end
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/spec/test_equipment/test_case/abst_test_case_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/test_case/abst_test_case'
4 |
5 | describe Bucky::TestEquipment::TestCase::AbstTestCase do
6 | Test::Unit::AutoRunner.need_auto_run = false
7 |
8 | let(:this_class) { Bucky::TestEquipment::TestCase::AbstTestCase }
9 | context 'not debug mode' do
10 | describe '.startup' do
11 | it 'call Bucky::Core::TestCore::TestResult.instance' do
12 | expect(Bucky::Core::TestCore::TestResult).to receive(:instance)
13 | this_class.startup
14 | end
15 | end
16 | describe '.shutdown' do
17 | let(:test_result_double) { double('test result double') }
18 | before do
19 | allow(Bucky::Core::TestCore::TestResult).to receive(:instance).and_return(test_result_double)
20 | this_class.startup
21 | end
22 | it 'call @@this_result.save' do
23 | expect(test_result_double).to receive(:save)
24 | this_class.shutdown
25 | end
26 | end
27 | describe '#teardown' do
28 | subject { this_class.new('test_method_name') }
29 | let(:added_result_info) { double('added_result_info_double') }
30 | let(:test_result_double) { double('this_result_double') }
31 | before do
32 | allow(Bucky::Core::TestCore::TestResult).to receive(:instance).and_return(test_result_double)
33 | allow(subject).to receive(:suite_id).and_return(1)
34 | allow(subject).to receive(:start_time).and_return(Time.now)
35 | this_class.startup
36 | this_class.class_variable_set('@@added_result_info', added_result_info)
37 | end
38 | it 'call @@added_result_info[]' do
39 | expect(added_result_info).to receive(:[]=)
40 | subject.teardown
41 | end
42 | end
43 | end
44 | context 'debug mode' do
45 | before do
46 | $debug = true
47 | end
48 |
49 | describe '.startup' do
50 | it 'not call Bucky::Core::TestCore::TestResult.instance' do
51 | expect(Bucky::Core::TestCore::TestResult).not_to receive(:instance)
52 | this_class.startup
53 | end
54 | end
55 | describe '.shutdown' do
56 | let(:test_result_double) { double('test result double') }
57 | before do
58 | allow(Bucky::Core::TestCore::TestResult).to receive(:instance).and_return(test_result_double)
59 | this_class.startup
60 | end
61 | it 'not call @@this_result.save' do
62 | expect(test_result_double).not_to receive(:save)
63 | this_class.shutdown
64 | end
65 | end
66 | describe '#teardown' do
67 | subject { this_class.new('test_method_name') }
68 | let(:elapsed_time_each_test_double) { double('elapsed_time_each_test_double') }
69 | before do
70 | this_class.startup
71 | allow(subject).to receive(:start_time).and_return(Time.now)
72 | this_class.class_variable_set('@@elapsed_time_each_test', elapsed_time_each_test_double)
73 | end
74 | it 'not call @@elapsed_time_each_test.push' do
75 | expect(elapsed_time_each_test_double).not_to receive(:push)
76 | subject.teardown
77 | end
78 | end
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/spec/tools/lint_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../lib/bucky/tools/lint'
4 |
5 | describe Bucky::Tools::Lint do
6 | let(:lint) { Bucky::Tools::Lint }
7 | let(:data_double) { { k1: 'v1', k2: { k3: 'v2' }, k4: { k5: { k6: 'v3', k7: 'v4' } } } }
8 | let(:rule_data_double) { { k1: 'v1', k2: { k3: 'v2' }, k4: { k5: { k6: 'v3', k8: 'v4' } } } }
9 | let(:config_dir) { "#{File.dirname(__FILE__)}/config_spec.yml" }
10 | let(:rule_config_dir) { "#{File.dirname(__FILE__)}/rule_config_spec.yml" }
11 | let(:dummy_dir) { '/hoge/*yml' }
12 |
13 | before do
14 | lint.class_variable_set(:@@config_dir, config_dir)
15 | lint.class_variable_set(:@@rule_config_dir, config_dir)
16 | end
17 |
18 | # [TODO] call method dynamically
19 | describe '#self.check' do
20 | it 'not raise exception, when argument is config' do
21 | lint.class_variable_set(:@@config_dir, config_dir)
22 | lint.class_variable_set(:@@rule_config_dir, config_dir)
23 | expect { lint.check('config') }.not_to raise_error
24 | end
25 | it 'raise exception, when call not exist method' do
26 | expect { lint.check('hoge') }.to raise_error(StandardError)
27 | end
28 | end
29 |
30 | describe '#check_config' do
31 | it 'print error message, when there is difference of hash' do
32 | lint.class_variable_set(:@@rule_config_dir, rule_config_dir)
33 | expect { lint.check_config }.to output("\e[31m[ERROR] The following configures are undefined. Tests can still be executed with default value automatically.\n- device_name_on_chrome-ipad\n{device_name_on_chrome-ipad: }\e[0m\n").to_stdout
34 | end
35 | it 'print ok, when there is no difference of hash' do
36 | lint.class_variable_set(:@@dir, config_dir)
37 | lint.class_variable_set(:@@rule_config_dir, config_dir)
38 | expect { lint.check_config }.to output("\e[32mok\e[0m\n").to_stdout
39 | end
40 | end
41 |
42 | describe '#merge_yaml_data' do
43 | it 'load yaml and make hash' do
44 | expect(lint.send(:merge_yaml_data, config_dir)).to be_a(Hash)
45 | end
46 | it 'when there is no key in yaml, raise exception' do
47 | expect { lint.send(:merge_yaml_data, dummy_dir) }.to raise_error(StandardError, "No key! please check the directory existence [#{dummy_dir}]")
48 | end
49 | end
50 |
51 | describe '#make_message' do
52 | it 'when there are element in given array, print error message' do
53 | expect { lint.send(:make_message, ['foo']) }.to output("\e[31m[ERROR] The following configures are undefined. Tests can still be executed with default value automatically.\n- foo\n{foo: }\e[0m\n").to_stdout
54 | end
55 | it 'when there are no element in given array, print ok' do
56 | expect { lint.send(:make_message, []) }.to output("\e[32mok\e[0m\n").to_stdout
57 | end
58 | end
59 |
60 | describe '#diff_arr' do
61 | it 'return diff array' do
62 | arr1 = %w[k1 k2-k3 k4-k5-k6 k4-k5-k7]
63 | arr2 = %w[k1 k2-k3]
64 | expect(lint.send(:diff_arr, arr1, arr2)).to eq %w[k4-k5-k6 k4-k5-k7]
65 | end
66 | end
67 |
68 | describe '#make_key_chain' do
69 | it 'return array containing elements of chain case' do
70 | expected_list = %w[k1 k2-k3 k4-k5-k6 k4-k5-k7]
71 | expect(lint.send(:make_key_chain, data_double)).to eq expected_list
72 | end
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/lib/bucky/core/test_core/test_result.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'singleton'
4 | require 'json'
5 | require 'test/unit/testresult'
6 |
7 | require_relative '../database/test_data_operator'
8 |
9 | module Bucky
10 | module Core
11 | module TestCore
12 | class TestResult
13 | include Singleton
14 |
15 | attr_accessor :result
16 |
17 | def initialize
18 | @result = Test::Unit::TestResult.new
19 | @tdo = Bucky::Core::Database::TestDataOperator.new
20 | end
21 |
22 | # Save test result
23 | # @param [Array] added_result_info
24 | def save(added_result_info)
25 | # Format data before saving to database.
26 | test_suite_result = format_result_summary(added_result_info)
27 | @tdo.save_test_result(test_suite_result)
28 | end
29 |
30 | private
31 |
32 | # Return Bucky::Test::Unit::TestResult Object
33 | # @param [Array] added_result_info
34 | # @return [Array] Array data for Sequel
35 | def format_result_summary(added_result_info)
36 | # For sequel
37 | data_set = {}
38 | error_test_case_name = []
39 |
40 | ##############################################
41 | # Store failed cases in data set. #
42 | ##############################################
43 | @result.faults.each do |fault|
44 | case_name = fault.method_name
45 | case_id = @tdo.get_test_case_id(added_result_info[case_name.to_sym][:test_suite_id], added_result_info[case_name.to_sym][:case_name])
46 | error_test_case_name.push(case_name.to_sym)
47 | data_set[case_name] =
48 | [
49 | id = nil,
50 | test_case_id = case_id,
51 | error_title = fault.message,
52 | error_message = fault.location.join("\n"),
53 | elapsed_time = added_result_info[case_name.to_sym][:elapsed_time],
54 | is_error = 1,
55 | job_id = $job_id,
56 | round = $round
57 | ]
58 | end
59 |
60 | ############################################
61 | # Store passed cases in data set. #
62 | ############################################
63 | added_result_info.delete_if { |k, _v| error_test_case_name.include?(k) }
64 | added_result_info.each do |case_name, added_info|
65 | case_id = @tdo.get_test_case_id(added_info[:test_suite_id], added_info[:case_name])
66 | data_set[case_name] =
67 | [
68 | id = nil,
69 | test_case_id = case_id,
70 | error_title = '',
71 | error_message = '',
72 | elapsed_time = added_info[:elapsed_time],
73 | is_error = 0,
74 | job_id = $job_id,
75 | round = $round
76 | ]
77 | end
78 |
79 | # Data set for sequel
80 | data_set_ary = data_set.map { |_, v| v }
81 |
82 | # Table colomn
83 | column_name_ary = %i[id test_case_id error_title error_message elapsed_time is_error job_id round]
84 | {
85 | column: column_name_ary,
86 | data_set: data_set_ary
87 | }
88 | end
89 | end
90 | end
91 | end
92 | end
93 |
--------------------------------------------------------------------------------
/spec/core/test_core/test_class_generator_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/test_core/test_class_generator'
4 | require 'test/unit'
5 | Test::Unit::AutoRunner.need_auto_run = false
6 |
7 | describe Bucky::Core::TestCore::TestClassGenerator.new(test_cond: 'for_test') do
8 | describe '#generate_test_class' do
9 | before do
10 | allow(Bucky::Utils::Config).to receive(:instance).and_return(linkstatus_open_timeout: 60)
11 | allow(Bucky::Utils::Config).to receive(:instance).and_return(linkstatus_read_timeout: 60)
12 | end
13 | let(:linkstatus_url_log) { { url: { code: 200, count: 1 } } }
14 | let(:linkstatus_data) do
15 | {
16 | test_class_name: 'SearchflowAreaTop',
17 | test_suite_name: 'searchflow_area_top',
18 | test_category: 'linkstatus',
19 | suite: {
20 | desc: 'check link on top page',
21 | device: device,
22 | service: 'foo',
23 | priority: 'high',
24 | test_category: 'linkstatus',
25 | cases: [
26 | {
27 | desc: 'top page',
28 | urls: ['http://localhost/hoge']
29 | }
30 | ]
31 | },
32 | test_suite_id: 6
33 | }
34 | end
35 | let(:e2e_data) do
36 | {
37 | test_class_name: 'SearchflowAreaTop',
38 | test_suite_name: 'searchflow_area_top',
39 | test_category: 'e2e',
40 | suite: {
41 | desc: 'description',
42 | device: 'pc',
43 | service: 'foo',
44 | priority: :high,
45 | test_category: :e2e,
46 | cases: [
47 | {
48 | desc: 'case_description',
49 | procs: [
50 | {
51 | proc: 'open toppage',
52 | exec:
53 | {
54 | operate: :go,
55 | url: 'http://localhost/'
56 | }
57 | }
58 | ]
59 | }
60 | ]
61 | }
62 | }
63 | end
64 |
65 | describe 'generate class' do
66 | let(:device) { 'pc' }
67 | it 'generate test class according to test suite data' do
68 | subject.generate_test_class(data: linkstatus_data, linkstatus_url_log: linkstatus_url_log)
69 | expect(subject.instance_variable_get(:@test_classes).const_get(:TestFooPcLinkstatusSearchflowAreaTop).name).to eq('Bucky::Core::TestCore::TestClasses::TestFooPcLinkstatusSearchflowAreaTop')
70 | end
71 | end
72 |
73 | describe 'generate method' do
74 | let(:device) { 'sp' }
75 | context 'in case of linkstatus' do
76 | it 'generate test method according to test suite data' do
77 | subject.generate_test_class(data: linkstatus_data, linkstatus_url_log: linkstatus_url_log)
78 | expect(subject.instance_variable_get(:@test_classes).const_get(:TestFooSpLinkstatusSearchflowAreaTop).instance_methods).to include :test_foo_sp_linkstatus_searchflow_area_top_0
79 | end
80 | end
81 |
82 | context 'in case of e2e' do
83 | it 'generate test method according to test suite data' do
84 | subject.generate_test_class(data: e2e_data)
85 | expect(subject.instance_variable_get(:@test_classes).const_get(:TestFooPcE2eSearchflowAreaTop).instance_methods).to include :test_foo_pc_e2e_searchflow_area_top_0
86 | end
87 | end
88 | end
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/pageobject/base_pageobject.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../utils/yaml_load'
4 | require_relative '../../core/exception/bucky_exception'
5 | require_relative '../selenium_handler/wait_handler'
6 |
7 | module Bucky
8 | module TestEquipment
9 | module PageObject
10 | class BasePageObject
11 | include Bucky::Utils::YamlLoad
12 | include Bucky::TestEquipment::SeleniumHandler::WaitHandler
13 |
14 | # https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/SearchContext.html#find_element-instance_method
15 | FINDERS = {
16 | class: 'class name',
17 | class_name: 'class name',
18 | css: 'css selector',
19 | id: 'id',
20 | link: 'link text',
21 | link_text: 'link text',
22 | name: 'name',
23 | partial_link_text: 'partial link text',
24 | tag_name: 'tag name',
25 | xpath: 'xpath'
26 | }.freeze
27 |
28 | def initialize(service, device, page_name, driver)
29 | @driver = driver
30 | generate_parts(service, device, page_name)
31 | end
32 |
33 | private
34 |
35 | # Load parts file and define parts method
36 | # @param [String] service
37 | # @param [String] device (pc, sp)
38 | # @param [String] page_name
39 | def generate_parts(service, device, page_name)
40 | Dir.glob("./services/#{service}/#{device}/parts/#{page_name}.yml").each do |file|
41 | parts_data = load_yaml(file)
42 | if parts_data.nil?
43 | warn("Warning: #{file} is empty")
44 | next
45 | end
46 | # Define part method e.g.) page.parts
47 | parts_data.each do |part_name, value|
48 | self.class.class_eval { define_method(part_name) { find_elem(value[0], value[1]) } }
49 | end
50 | end
51 | end
52 |
53 | # @param [String] method Method name for searching WebElement
54 | # http://www.rubydoc.info/gems/selenium-webdriver/0.0.28/Selenium/WebDriver/Find
55 | # @param [String] Condition of search (xpath/id)
56 | # @return [Selenium::WebDriver::Element]
57 | def find_elem(method, value)
58 | method_name = method.downcase.to_sym
59 | raise StandardError, "Invalid finder. #{method_name}" unless FINDERS.key? method_name
60 |
61 | # wait until driver find element
62 | elements = wait_until_helper(3, 0.1, Selenium::WebDriver::Error::NoSuchElementError) { @driver.find_elements(method_name, value) }
63 | raise_if_elements_empty(elements, method_name, value)
64 |
65 | get_element_or_attribute = lambda do |elems, arg|
66 | # return WebElement
67 | return elems[arg] if arg.is_a? Integer
68 | # return String(Value of WebElement`s attribute)
69 | return elems.first.attribute(arg) if [String, Symbol].include? arg.class
70 |
71 | raise StandardError, "Invalid argument type. Expected type is Integer/String/Symbol.\n\
72 | | Got argument:#{arg}, type:#{arg.class}."
73 | end
74 |
75 | elements.first.instance_eval do
76 | define_singleton_method('[]') { |arg| get_element_or_attribute.call(elements, arg) }
77 | %w[each map length].each { |m| define_singleton_method(m) { |&block| elements.send(m, &block) } }
78 | end
79 |
80 | elements.first
81 | end
82 |
83 | def raise_if_elements_empty(elements, method, value)
84 | raise Selenium::WebDriver::Error::NoSuchElementError, "#{method} #{value}" if elements.empty?
85 | end
86 | end
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/spec/test_equipment/pageobject/base_pageobject_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/pageobject/base_pageobject'
4 | require 'selenium-webdriver'
5 |
6 | describe Bucky::TestEquipment::PageObject::BasePageObject do
7 | subject { Bucky::TestEquipment::PageObject::BasePageObject.new('a', 'b', 'c', driver) }
8 | let(:driver) { double('driver double') }
9 | describe '#initialize' do
10 | it 'not raise exeption' do
11 | expect { subject }.not_to raise_error
12 | end
13 | end
14 | describe '#find_elem' do
15 | let(:value) { 'content-1' }
16 | let(:method_mock) { 'id' }
17 | let(:config_double) { double('double of Config') }
18 | let(:element) do
19 | class Element end
20 | return Element.new
21 | end
22 | let(:elem_objects) { [element, element.dup] }
23 | context 'If given method name is included in FINDERS' do
24 | %w[class class_name css id link link_text name partial_link_text tag_name xpath].each do |method|
25 | it "If #{method} is given, find_elements(:#{method}, value) is call" do
26 | expect(driver).to receive(:find_elements).with(method.to_sym, value).and_return(elem_objects)
27 | subject.send(:find_elem, method, value)
28 | end
29 | end
30 | context 'Redefine Array methods in WebElement' do
31 | let(:method) { :xpath }
32 | before do
33 | allow(driver).to receive(:find_elements).with(method, value).and_return(elem_objects)
34 | end
35 | %w[[] each map length].each do |redefined_method|
36 | it "#{redefined_method} method is redefined in WebElement instance" do
37 | elem = subject.send(:find_elem, method, value)
38 | expect(elem).to respond_to(redefined_method.to_sym)
39 | end
40 | end
41 | context '[] method' do
42 | it '[] method is called in multipule WebElements object, if [] method called with integers.' do
43 | elem = subject.send(:find_elem, method, value)
44 | expect(elem_objects).to receive(:[])
45 | elem[1]
46 | end
47 | it 'Attribute method is called in a single WebElement, if [] method called with string.' do
48 | elem = subject.send(:find_elem, method, value)
49 | expect(element).to receive(:attribute)
50 | elem['attribute_name']
51 | end
52 | it 'Attribute method is called in a single WebElement, if [] method called with symbol.' do
53 | elem = subject.send(:find_elem, method, value)
54 | expect(element).to receive(:attribute)
55 | elem[:attribute_name]
56 | end
57 | it 'Raise invalid argument type error, if [] method called with anything other than integers, strings, or symbols.' do
58 | elem = subject.send(:find_elem, method, value)
59 | expect { elem[[]] }.to raise_error(StandardError)
60 | end
61 | end
62 | end
63 | end
64 | it 'If given method name is not included in FINDERS, raise error' do
65 | method = 'invalid method name'
66 | allow(Bucky::Core::Exception::BuckyException).to receive(:handle)
67 | expect { subject.send(:find_elem, method, value) }.to raise_error(StandardError, "Invalid finder. #{method}")
68 | end
69 | it 'raise StandardError when exception' do
70 | allow(Bucky::Utils::Config).to receive(:instance).and_return(config_double)
71 | allow(config_double).to receive('[]').and_return(bucky_error: 'test')
72 | allow(driver).to receive(:find_elements).and_raise(Selenium::WebDriver::Error::NoSuchElementError.new)
73 | expect { subject.send(:find_elem, method_mock, value) }.to raise_error(StandardError)
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | bucky-core (0.10.25)
5 | addressable (~> 2.5)
6 | color_echo (~> 3.1)
7 | json (~> 2.3.0)
8 | nokogiri (~> 1.11.1)
9 | parallel (~> 1.11)
10 | ruby-mysql (~> 2.9)
11 | selenium-webdriver (= 4.24)
12 | sequel (= 5.84)
13 | test-unit (~> 3.2)
14 |
15 | GEM
16 | remote: https://rubygems.org/
17 | specs:
18 | addressable (2.8.7)
19 | public_suffix (>= 2.0.2, < 7.0)
20 | ansi (1.5.0)
21 | ast (2.4.2)
22 | awesome_print (1.9.2)
23 | base64 (0.2.0)
24 | bigdecimal (3.1.8)
25 | binding_of_caller (1.0.1)
26 | debug_inspector (>= 1.2.0)
27 | byebug (11.1.3)
28 | coderay (1.1.3)
29 | color_echo (3.1.1)
30 | debug_inspector (1.2.0)
31 | diff-lcs (1.5.1)
32 | docile (1.1.5)
33 | hirb (0.7.3)
34 | json (2.3.1)
35 | language_server-protocol (3.17.0.3)
36 | logger (1.6.1)
37 | method_source (1.1.0)
38 | mini_portile2 (2.5.3)
39 | nokogiri (1.11.7)
40 | mini_portile2 (~> 2.5.0)
41 | racc (~> 1.4)
42 | parallel (1.26.3)
43 | parser (3.3.5.0)
44 | ast (~> 2.4.1)
45 | racc
46 | power_assert (2.0.3)
47 | pry (0.14.2)
48 | coderay (~> 1.1)
49 | method_source (~> 1.0)
50 | pry-byebug (3.10.1)
51 | byebug (~> 11.0)
52 | pry (>= 0.13, < 0.15)
53 | pry-stack_explorer (0.6.1)
54 | binding_of_caller (~> 1.0)
55 | pry (~> 0.13)
56 | public_suffix (6.0.1)
57 | racc (1.8.1)
58 | rainbow (3.1.1)
59 | rake (13.2.1)
60 | regexp_parser (2.9.2)
61 | rexml (3.3.7)
62 | rspec (3.13.0)
63 | rspec-core (~> 3.13.0)
64 | rspec-expectations (~> 3.13.0)
65 | rspec-mocks (~> 3.13.0)
66 | rspec-core (3.13.1)
67 | rspec-support (~> 3.13.0)
68 | rspec-expectations (3.13.3)
69 | diff-lcs (>= 1.2.0, < 2.0)
70 | rspec-support (~> 3.13.0)
71 | rspec-mocks (3.13.1)
72 | diff-lcs (>= 1.2.0, < 2.0)
73 | rspec-support (~> 3.13.0)
74 | rspec-support (3.13.1)
75 | rspec_junit_formatter (0.6.0)
76 | rspec-core (>= 2, < 4, != 2.12.0)
77 | rubocop (1.66.1)
78 | json (~> 2.3)
79 | language_server-protocol (>= 3.17.0)
80 | parallel (~> 1.10)
81 | parser (>= 3.3.0.2)
82 | rainbow (>= 2.2.2, < 4.0)
83 | regexp_parser (>= 2.4, < 3.0)
84 | rubocop-ast (>= 1.32.2, < 2.0)
85 | ruby-progressbar (~> 1.7)
86 | unicode-display_width (>= 2.4.0, < 3.0)
87 | rubocop-ast (1.32.3)
88 | parser (>= 3.3.1.0)
89 | ruby-mysql (2.11.1)
90 | ruby-progressbar (1.13.0)
91 | rubyzip (2.3.2)
92 | selenium-webdriver (4.24.0)
93 | base64 (~> 0.2)
94 | logger (~> 1.4)
95 | rexml (~> 3.2, >= 3.2.5)
96 | rubyzip (>= 1.2.2, < 3.0)
97 | websocket (~> 1.0)
98 | sequel (5.84.0)
99 | bigdecimal
100 | simplecov (0.15.1)
101 | docile (~> 1.1.0)
102 | json (>= 1.8, < 3)
103 | simplecov-html (~> 0.10.0)
104 | simplecov-console (0.4.2)
105 | ansi
106 | hirb
107 | simplecov
108 | simplecov-html (0.10.2)
109 | test-unit (3.6.2)
110 | power_assert
111 | unicode-display_width (2.6.0)
112 | websocket (1.2.11)
113 |
114 | PLATFORMS
115 | ruby
116 |
117 | DEPENDENCIES
118 | awesome_print (~> 1.8)
119 | bucky-core!
120 | bundler (= 2.5.18)
121 | hirb (~> 0.7)
122 | pry (~> 0.10)
123 | pry-byebug (~> 3.4)
124 | pry-stack_explorer (~> 0.4)
125 | rake (~> 13)
126 | rspec (~> 3.6)
127 | rspec_junit_formatter (~> 0.3)
128 | rubocop (= 1.66.1)
129 | simplecov (~> 0.15.1)
130 | simplecov-console (~> 0.4.2)
131 |
132 | BUNDLED WITH
133 | 2.5.18
134 |
--------------------------------------------------------------------------------
/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/pc_e2e.yml:
--------------------------------------------------------------------------------
1 | desc: pc e2e suits
2 | device: pc
3 | service: service_a
4 | priority: high
5 | test_category: e2e
6 | cases:
7 | - case_name: pc_e2e_1
8 | func: pc e2e 1 func
9 | desc: pc e2e 1 func
10 | procs:
11 | - proc: open index
12 | exec:
13 | operate: go
14 | url: http://bucky.net
15 | - proc: open index
16 | exec:
17 | operate: click
18 | page: index
19 | part: link
20 | timeout: 1
21 | - proc: check title
22 | exec:
23 | verify: assert_title
24 | expect: Test Page
25 | - case_name: pc_e2e_2
26 | func: pc e2e 2 func
27 | desc: pc e2e 2 func
28 | procs:
29 | - proc: open index
30 | exec:
31 | operate: go
32 | url: http://bucky.net
33 | - proc: open index
34 | exec:
35 | operate: click
36 | page: index
37 | part: link
38 | - proc: check title(NG)
39 | exec:
40 | verify: assert_title
41 | expect: Test Page Wrong
42 | - case_name: pc_e2e_3
43 | func: pc e2e 3 func UserAgent
44 | desc: pc e2e 3 func UserAgent
45 | procs:
46 | - proc: open index
47 | exec:
48 | operate: go
49 | url: http://bucky.net
50 | - proc: check UserAgent(OS)
51 | exec:
52 | verify: assert_contained_text
53 | page: index
54 | part: ua
55 | expect: Linux
56 | - case_name: pc_e2e_4
57 | func: pc e2e 4 func when
58 | desc: pc e2e 4 func when
59 | procs:
60 | - proc: open index
61 | exec:
62 | operate: go
63 | url: http://bucky.net
64 | - proc: index
65 | exec:
66 | operate: click
67 | page: index
68 | part: link
69 | when: <%= ENV['STAGE'] == 'development' %>
70 | - case_name: pc_e2e_5
71 | func: pc e2e 5 multi elements
72 | desc: pc e2e 5 multi elements
73 | procs:
74 | - proc: open index
75 | exec:
76 | operate: go
77 | url: http://bucky.net
78 | - proc: index
79 | exec:
80 | operate: click
81 | page: index
82 | part:
83 | locate: links
84 | num: 1
85 | - case_name: pc_e2e_6
86 | func: pc e2e 6 get element from PageObject
87 | desc: pc e2e 6 click the single element from PageObject
88 | procs:
89 | - proc: open index
90 | exec:
91 | operate: go
92 | url: http://bucky.net
93 | - proc: index
94 | exec:
95 | operate: click_single_element
96 | page: index
97 | - case_name: pc_e2e_7
98 | func: pc e2e 7 get multiple element from PageObject
99 | desc: pc e2e 7 click the second element in multiple element from PageObject
100 | procs:
101 | - proc: open index
102 | exec:
103 | operate: go
104 | url: http://bucky.net
105 | - proc: index
106 | exec:
107 | operate: click_multiple_element
108 | page: index
109 | - case_name: pc_e2e_8
110 | func: pc e2e 8 get element from Verifications
111 | desc: pc e2e 8 click the single element from Verifications
112 | procs:
113 | - proc: open index
114 | exec:
115 | operate: go
116 | url: http://bucky.net
117 | - proc: index
118 | exec:
119 | verify: click_single_element
120 | page: index
121 | - case_name: pc_e2e_9
122 | func: pc e2e 9 get multiple element from Verifications
123 | desc: pc e2e 9 click the second element in multiple element from Verifications
124 | procs:
125 | - proc: open index
126 | exec:
127 | operate: go
128 | url: http://bucky.net
129 | - proc: index
130 | exec:
131 | verify: click_multiple_element
132 | page: index
133 |
--------------------------------------------------------------------------------
/spec/test_equipment/test_case/e2e_test_case_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/test_case/e2e_test_case'
4 |
5 | describe Bucky::TestEquipment::TestCase::E2eTestCase do
6 | Test::Unit::AutoRunner.need_auto_run = false
7 | let(:this_class) { Bucky::TestEquipment::TestCase::E2eTestCase }
8 | subject { this_class.new('test_method_name') }
9 |
10 | before do
11 | allow(subject).to receive(:suite_data).and_return(service: 'service_a', device: 'pc')
12 | end
13 |
14 | describe '#t_equip_setup' do
15 | it 'call create_webdriver' do
16 | allow(Bucky::TestEquipment::PageObject::Pages).to receive(:new)
17 | allow(Bucky::TestEquipment::Verifications::ServiceVerifications).to receive(:new)
18 | allow(Bucky::TestEquipment::UserOperation::UserOperator).to receive(:new)
19 | expect(subject).to receive(:create_webdriver)
20 | subject.t_equip_setup
21 | end
22 | it 'create instance of Bucky::TestEquipment::PageObject::Pages' do
23 | allow(subject).to receive(:create_webdriver)
24 | allow(Bucky::TestEquipment::Verifications::ServiceVerifications).to receive(:new)
25 | allow(Bucky::TestEquipment::UserOperation::UserOperator).to receive(:new)
26 | expect(Bucky::TestEquipment::PageObject::Pages).to receive(:new)
27 | subject.t_equip_setup
28 | end
29 | it 'create instance of Bucky::TestEquipment::UserOperation::UserOperator' do
30 | allow(subject).to receive(:create_webdriver)
31 | allow(Bucky::TestEquipment::Verifications::ServiceVerifications).to receive(:new)
32 | allow(Bucky::TestEquipment::PageObject::Pages).to receive(:new)
33 | expect(Bucky::TestEquipment::UserOperation::UserOperator).to receive(:new)
34 | subject.t_equip_setup
35 | end
36 | it 'create instance of Bucky::TestEquipment::Verifications::ServiceVerifications' do
37 | allow(subject).to receive(:create_webdriver)
38 | allow(Bucky::TestEquipment::PageObject::Pages).to receive(:new)
39 | allow(Bucky::TestEquipment::UserOperation::UserOperator).to receive(:new)
40 | expect(Bucky::TestEquipment::Verifications::ServiceVerifications).to receive(:new)
41 | subject.t_equip_setup
42 | end
43 | end
44 |
45 | describe '#verify' do
46 | let(:verification_double) { double('verification double') }
47 | let(:verify_args) { { exec: { verify: 'assert_title', expect: 'page title' }, step_number: 1, proc_name: 'test proc' } }
48 | before do
49 | allow(subject).to receive(:create_webdriver)
50 | allow(Bucky::TestEquipment::Verifications::ServiceVerifications).to receive(:new).and_return(verification_double)
51 | allow(Bucky::TestEquipment::PageObject::Pages).to receive(:new)
52 | allow(Bucky::TestEquipment::UserOperation::UserOperator).to receive(:new)
53 | subject.t_equip_setup
54 | end
55 | it 'call service_verifications.send' do
56 | expect(verification_double).to receive(:send)
57 | subject.verify(**verify_args)
58 | end
59 | end
60 |
61 | describe '#operate' do
62 | let(:user_operator_double) { double('user_operator double') }
63 | let(:op_args) { { exec: { operate: 'click', page: 'top', part: { locate: 'rosen_tokyo', num: 1 } }, step_number: 1, proc_name: 'test proc' } }
64 | before do
65 | allow(subject).to receive(:create_webdriver)
66 | allow(Bucky::TestEquipment::Verifications::ServiceVerifications).to receive(:new)
67 | allow(Bucky::TestEquipment::PageObject::Pages).to receive(:new)
68 | allow(Bucky::TestEquipment::UserOperation::UserOperator).to receive(:new).and_return(user_operator_double)
69 | allow(Bucky::TestEquipment::Verifications::E2eVerification).to receive(:new)
70 | subject.t_equip_setup
71 | end
72 | it 'call user_operator.send' do
73 | expect(user_operator_double).to receive(:send)
74 | subject.operate(**op_args)
75 | end
76 | end
77 |
78 | describe '#setup' do
79 | it 'call t_equip_setup' do
80 | expect(subject).to receive(:t_equip_setup)
81 | subject.setup
82 | end
83 | end
84 | describe '#teardown' do
85 | # Not implement, because implementing super mock is difficault
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/user_operation/user_operation_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../selenium_handler/wait_handler'
4 |
5 | module Bucky
6 | module TestEquipment
7 | module UserOperation
8 | class UserOperationHelper
9 | include Bucky::TestEquipment::SeleniumHandler::WaitHandler
10 |
11 | def initialize(args)
12 | @app = args[:app]
13 | @device = args[:device]
14 | @driver = args[:driver]
15 | @pages = args[:pages]
16 | end
17 |
18 | # Open url
19 | # @param [Hash]
20 | def go(args)
21 | @driver.navigate.to args[:url]
22 | end
23 |
24 | def back(_)
25 | @driver.navigate.back
26 | end
27 |
28 | def input(args)
29 | # when input successfully, return of click is nil.
30 | wait_until_helper((args || {}).fetch(:timeout, 5), 0.1, Selenium::WebDriver::Error::StaleElementReferenceError) { @pages.get_part(args).send_keys(args[:word]).nil? }
31 | end
32 |
33 | # Clear textbox
34 | def clear(args)
35 | @pages.get_part(args).clear
36 | end
37 |
38 | def click(args)
39 | elem = @pages.get_part(args)
40 | # when click successfully, return of click is nil.
41 | wait_until_helper((args || {}).fetch(:timeout, 5), 0.1, Selenium::WebDriver::Error::WebDriverError) { elem.click.nil? }
42 | end
43 |
44 | def refresh(_)
45 | @driver.navigate.refresh
46 | end
47 |
48 | def switch_to_next_window(_)
49 | window_index = @driver.window_handles.index(@driver.window_handle)
50 | windows_number = @driver.window_handles.size
51 | return if window_index + 1 == windows_number
52 |
53 | @driver.switch_to.window(@driver.window_handles[window_index + 1])
54 | end
55 |
56 | def switch_to_previous_window(_)
57 | window_index = @driver.window_handles.index(@driver.window_handle)
58 | @driver.switch_to.window(@driver.window_handles[window_index - 1])
59 | end
60 |
61 | def switch_to_newest_window(_)
62 | @driver.switch_to.window(@driver.window_handles.last)
63 | end
64 |
65 | def switch_to_oldest_window(_)
66 | @driver.switch_to.window(@driver.window_handles.first)
67 | end
68 |
69 | def switch_to_the_window(args)
70 | # when the window successfully switched, return of switch_to.window is nil.
71 | wait_until_helper((args || {}).fetch(:timeout, 5), 0.1, Selenium::WebDriver::Error::NoSuchWindowError) { @driver.switch_to.window(args[:window_name]).nil? }
72 | end
73 |
74 | # Close window
75 | def close(_)
76 | window_index = @driver.window_handles.index(@driver.window_handle)
77 | @driver.close
78 | @driver.switch_to.window(@driver.window_handles[window_index - 1])
79 | end
80 |
81 | def stop(_)
82 | puts 'stop. press enter to continue'
83 | gets
84 | end
85 |
86 | def choose(args)
87 | option = wait_until_helper((args || {}).fetch(:timeout, 5), 0.1, Selenium::WebDriver::Error::StaleElementReferenceError) { Selenium::WebDriver::Support::Select.new(@pages.get_part(args)) }
88 | if args.key?(:text)
89 | type = :text
90 | selected = args[type].to_s
91 | elsif args.key?(:value)
92 | type = :value
93 | selected = args[type].to_s
94 | elsif args.key?(:index)
95 | type = :index
96 | selected = args[type].to_i
97 | else
98 | raise StandardError, "Included invalid key #{args.keys}"
99 | end
100 | option.select_by(type, selected)
101 | end
102 |
103 | # Alert accept
104 | def accept_alert(args)
105 | alert = wait_until_helper((args || {}).fetch(:timeout, 5), 0.1, Selenium::WebDriver::Error::NoSuchAlertError) { @driver.switch_to.alert }
106 | alert.accept
107 | end
108 |
109 | def wait(args)
110 | # Indent
111 | print ' ' * 6
112 | args[:sec].times do |count|
113 | print "#{count + 1} "
114 | sleep 1
115 | end
116 | puts ''
117 | end
118 | end
119 | end
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/spec/test_equipment/user_operation/user_operator_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/user_operation/user_operator'
4 |
5 | describe Bucky::TestEquipment::UserOperation::UserOperator do
6 | let(:pages_double) { double('pages double') }
7 | let(:user_operation_helper_double) { double('user_operation_helper double') }
8 | let(:uset_operator_args) { { service: 'sample_app', device: 'pc', driver: { driver: 'mock driver' }, pages: pages_double } }
9 |
10 | subject { Bucky::TestEquipment::UserOperation::UserOperator.new(uset_operator_args) }
11 | before do
12 | allow(Bucky::Utils::BuckyLogger).to receive(:write)
13 | end
14 |
15 | describe '#method_missing' do
16 | before do
17 | allow(Bucky::TestEquipment::UserOperation::UserOperationHelper).to receive(:new).and_return(user_operation_helper_double)
18 | allow(user_operation_helper_double).to receive_message_chain(:methods, :include?).and_return(operation_helper_methods?)
19 | end
20 | context 'when call method of operation helper' do
21 | let(:operation_helper_methods?) { true }
22 | let(:operation) { :go }
23 | let(:operation_args) { { exec: { operation: 'go' }, step_number: 1, proc_name: 'test proc' } }
24 | it 'call operation_helper.send' do
25 | expect(user_operation_helper_double).to receive(:send)
26 | subject.send(operation, 'test_method_name', **operation_args)
27 | end
28 | end
29 | context 'when call method of other than operation helper' do
30 | let(:page_double) { double('page double') }
31 | let(:part_double) { double('part double') }
32 | let(:operation_helper_methods?) { false }
33 | let(:operation) { :go }
34 | let(:operation_args) { { exec: { operation: 'go' }, step_number: 1, proc_name: 'test proc' } }
35 |
36 | it 'not call operation_helper.send' do
37 | expect(user_operation_helper_double).not_to receive(:send)
38 | subject.send(operation, 'test_method_name', **operation_args)
39 | end
40 |
41 | context 'when call method of pageobject' do
42 | let(:operation) { :input_inquire_info }
43 | let(:operation_args) { { exec: { page: 'top', operate: 'input_inquire_info' }, step_number: 1, proc_name: 'test proc' } }
44 | it 'call pageobject.send' do
45 | allow(pages_double).to receive(:send).and_return(page_double)
46 | expect(page_double).to receive(:send)
47 | subject.send(operation, 'test_method_name', **operation_args)
48 | end
49 | end
50 | context 'when call method of part' do
51 | let(:operation) { :click }
52 | context 'in case single part' do
53 | let(:operation_args) { { exec: { page: 'top', part: 'rosen_tokyo' }, step_number: 1, proc_name: 'test proc' } }
54 | it 'call send of part object' do
55 | allow(pages_double).to receive(:send).and_return(page_double)
56 | allow(page_double).to receive(:send).and_return(part_double)
57 | expect(part_double).to receive(:send).with(operation)
58 | subject.send(operation, 'test_method_name', **operation_args)
59 | end
60 | end
61 | context 'in case operate one part of multiple parts' do
62 | let(:operation_args) { { exec: { page: 'top', part: { locate: 'rosen_tokyo', num: 1 } }, step_number: 1, proc_name: 'test proc' } }
63 | let(:parts_double) { double('parts double') }
64 | it 'call send of part object' do
65 | allow(pages_double).to receive(:send).and_return(page_double)
66 | allow(page_double).to receive(:send).and_return(parts_double)
67 | allow(parts_double).to receive(:[]).and_return(part_double)
68 | expect(part_double).to receive(:send)
69 | subject.send(operation, 'test_method_name', **operation_args)
70 | end
71 | end
72 | end
73 | end
74 | context 'when raise exception' do
75 | let(:operation_helper_methods?) { true }
76 | let(:operation) { :go }
77 | let(:operation_args) { { exec: { operation: 'go' }, step_number: 1, proc_name: 'test proc' } }
78 | let(:exception) { StandardError }
79 | it 'call WebdrverException.handle' do
80 | allow(user_operation_helper_double).to receive(:send).and_raise(exception)
81 | expect { subject.send(operation, 'test_method_name', operation_args) }.to raise_error(exception)
82 | end
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/system_testing/testing_code/e2e.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | setup() {
4 | cd /bucky-core/system_testing/test_bucky_project
5 | }
6 |
7 | @test "[e2e] #01 After executing e2e operate go, results have no failures nor errors" {
8 | run bucky run -t e2e -d -D pc -c pc_e2e_1
9 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
10 | }
11 |
12 | @test "[e2e] #02 After executing e2e verify assert_title, results contain verify words, no failures/errors and exit code is 0" {
13 | run bucky run -t e2e -d -D pc -c pc_e2e_1
14 | [ $status -eq 0 ]
15 | [ $(expr "$output" : ".*:verify.*assert_title.*") -ne 0 ]
16 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
17 | }
18 |
19 | @test "[e2e] #03 After executing e2e verify assert_title, results contain verify words and 1 failure and exit code is 1" {
20 | run bucky run -t e2e -d -D pc -c pc_e2e_2
21 | [ $status -eq 1 ]
22 | [ $(expr "$output" : ".*:verify.*assert_title.*") -ne 0 ]
23 | [ $(expr "$output" : ".*1 failures.*") -ne 0 ]
24 | }
25 |
26 | @test "[e2e] #04 After executing e2e on pc , results contain 'Linux' and no failures/errors." {
27 | run bucky run -t e2e -d -D pc -c pc_e2e_3
28 | [ $(expr "$output" : ".*Linux.*") -ne 0 ]
29 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
30 | }
31 |
32 | @test "[e2e] #05 After executing e2e on sp , results contain 'iPhone' and no failures/errors." {
33 | run bucky run -t e2e -d -D sp -c sp_e2e_1
34 | [ $(expr "$output" : ".*iPhone.*") -ne 0 ]
35 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
36 | }
37 |
38 | @test "[e2e] #06 After executing e2e on tablet , results contain 'iPad' and no failures/errors." {
39 | run bucky run -t e2e -d -D tablet -c tablet_e2e_1
40 | [ $(expr "$output" : ".*iPad.*") -ne 0 ]
41 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
42 | }
43 |
44 | @test "[e2e] #07 After executing e2e operate go with setup each, results have no failures nor errors" {
45 | run bucky run -d -c setup_each_pc_e2e_1
46 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
47 | }
48 |
49 | @test "[e2e] #08 After executing e2e operate go with teardown each, results have no failures nor errors" {
50 | run bucky run -d -c teardown_each_pc_e2e_1
51 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
52 | }
53 |
54 | @test "[e2e] #09 When setup and teardown is added, multiple test cases execute normally, results have no failures nor errors" {
55 | run bucky run -d -s setup_teardown_each_pc_e2e
56 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
57 | }
58 |
59 | @test "[e2e] #10 When 'when condition' is added, results have no failures nor errors" {
60 | export STAGE=development
61 | run bucky run -t e2e -d -D pc -c pc_e2e_4
62 | [ $(expr "$output" : ".*click.*") -ne 0 ]
63 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
64 | }
65 |
66 | @test "[e2e] #11 When 'when condition' is not added, results have no click procedure" {
67 | run bucky run -t e2e -d -D pc -c pc_e2e_4
68 | [ $(expr "$output" : ".*click.*") -eq 0 ]
69 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
70 | }
71 |
72 | @test "[e2e] #12 When 'different when condition' is added, results have no click procedure" {
73 | export STAGE=staging
74 | run bucky run -t e2e -d -D pc -c pc_e2e_4
75 | [ $(expr "$output" : ".*click.*") -eq 0 ]
76 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
77 | }
78 |
79 | @test "[e2e] #13 When click second element from scenario, results have no failures nor errors" {
80 | run bucky run -t e2e -d -D pc -c pc_e2e_5
81 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
82 | }
83 |
84 | @test "[e2e] #14 When click element from PageObject file, results have no failures nor errors" {
85 | run bucky run -t e2e -d -D pc -c pc_e2e_6
86 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
87 | }
88 |
89 | @test "[e2e] #15 When click second element from PageObject file, results have no failures nor errors" {
90 | run bucky run -t e2e -d -D pc -c pc_e2e_7
91 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
92 | }
93 |
94 | @test "[e2e] #16 When click element from Verification file, results have no failures nor errors" {
95 | run bucky run -t e2e -d -D pc -c pc_e2e_8
96 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
97 | }
98 |
99 | @test "[e2e] #17 When click second element from Verification file, results have no failures nor errors" {
100 | run bucky run -t e2e -d -D pc -c pc_e2e_9
101 | [ $(expr "$output" : ".*0 failures, 0 errors,.*") -ne 0 ]
102 | }
103 |
--------------------------------------------------------------------------------
/spec/core/test_core/test_manager_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/core/test_core/test_manager'
4 | require 'test/unit'
5 | Test::Unit::AutoRunner.need_auto_run = false
6 |
7 | describe Bucky::Core::TestCore::TestManager do
8 | # Class Double
9 | let(:tdo) { double('tdo-double') }
10 | before do
11 | allow(Bucky::Core::Database::TestDataOperator).to receive(:new).and_return(tdo)
12 | allow(tdo).to receive(:save_job_record_and_get_job_id)
13 | allow(tdo).to receive(:get_ng_test_cases_at_last_execution).and_return(ng_case_data)
14 | allow(tm).to receive(:do_test_suites).and_return({})
15 | end
16 |
17 | describe '#run' do
18 | let(:tm) { Bucky::Core::TestCore::TestManager.new(re_test_count: re_test_count) }
19 |
20 | describe '@re_test_count: run untill max round' do
21 | context '@re_test_count is 1' do
22 | let(:re_test_count) { 1 }
23 | let(:ng_case_data) { { test_case: 1 } }
24 | it 'call load_test_suites once' do
25 | expect(tm).to receive(:load_test_suites).once
26 | tm.run
27 | end
28 | end
29 |
30 | context '@re_test_count is 2' do
31 | let(:re_test_count) { 2 }
32 | let(:ng_case_data) { { test_case: 1 } }
33 | it 'call load_test_suites twice' do
34 | expect(tm).to receive(:load_test_suites).twice
35 | tm.run
36 | end
37 | end
38 | end
39 |
40 | describe '@tdo.get_ng_test_cases_at_last_execution' do
41 | context 'there is no failure' do
42 | let(:re_test_count) { 2 }
43 | let(:ng_case_data) { {} }
44 | it 'call load_test_suites once' do
45 | expect(tm).to receive(:load_test_suites).once
46 | tm.run
47 | end
48 | end
49 | end
50 | end
51 |
52 | describe '#rerun' do
53 | let(:tm) { Bucky::Core::TestCore::TestManager.new(re_test_count: re_test_count, job: rerun_job_id) }
54 | before do
55 | allow(tdo).to receive(:get_last_round_from_job_id)
56 | end
57 |
58 | describe 'call execute_test on rerun method' do
59 | context 'there is no failure in round 2' do
60 | let(:re_test_count) { 2 }
61 | let(:rerun_job_id) { 10 }
62 | let(:ng_case_data) { {} }
63 | it 'call execute_test' do
64 | expect(tm).to receive(:execute_test)
65 | tm.rerun
66 | end
67 | end
68 | end
69 | end
70 |
71 | describe '#parallel_helper' do
72 | let(:tm) { Bucky::Core::TestCore::TestManager.new(re_test_count: 1) }
73 | let(:parallel_helper) { Class.new { extend Bucky::Core::TestCore::ParallelHelper } }
74 | let(:block_mock) { proc { 'Block mock' } }
75 | let(:ng_case_data) { {} }
76 | before do
77 | $debug = true
78 | end
79 | after do
80 | $debug = false
81 | end
82 | context 'parallel_new_worker_each' do
83 | let(:e2e_parallel_num) { 2 }
84 | let(:test_suite_data_e2e) do
85 | [
86 | { test_class_name: 'test',
87 | test_suite_name: 'test',
88 | test_category: 'e2e',
89 | suite: {
90 | device: 'pc',
91 | service: 'spec',
92 | test_category: 'e2e',
93 | cases: [
94 | { case_name: 'test_1' }
95 | ]
96 | } }
97 | ]
98 | end
99 |
100 | it 'call target block in fork' do
101 | allow(parallel_helper).to receive(:fork) do |&block|
102 | expect(block.call).to eq('Block mock')
103 | end
104 | parallel_helper.send(:parallel_new_worker_each, test_suite_data_e2e, e2e_parallel_num, &block_mock)
105 | end
106 | end
107 |
108 | context 'parallel_distribute_into_workers' do
109 | let(:linkstatus_parallel_num) { 2 }
110 | let(:test_suite_data_linkstatus) do
111 | [
112 | { test_class_name: 'test',
113 | test_suite_name: 'test',
114 | test_category: 'linkstatus',
115 | suite: {
116 | device: 'pc',
117 | service: 'spec',
118 | test_category: 'linkstatus',
119 | cases: [
120 | { case_name: 'test_1' }
121 | ]
122 | } }
123 | ]
124 | end
125 | it 'make workers in fork then call block in each' do
126 | allow(parallel_helper).to receive(:fork) do |&block_fork|
127 | allow(block_fork.call).to receive(:each) do |&block_each|
128 | expect(block_each.call).to eq('Block mock')
129 | end
130 | end
131 | parallel_helper.send(:parallel_distribute_into_workers, test_suite_data_linkstatus, linkstatus_parallel_num, &block_mock)
132 | end
133 | end
134 | end
135 | end
136 |
--------------------------------------------------------------------------------
/lib/bucky/test_equipment/verifications/e2e_verification.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../utils/bucky_logger'
4 | require_relative '../../utils/bucky_output'
5 | require_relative './abst_verification'
6 | require_relative '../evidence/evidence_generator'
7 |
8 | module Bucky
9 | module TestEquipment
10 | module Verifications
11 | class E2eVerification < Bucky::TestEquipment::Verifications::AbstVerification
12 | include Bucky::Utils::BuckyLogger
13 | include Bucky::Utils::BuckyOutput
14 | using StringColorize
15 |
16 | def initialize(driver, pages, test_case_name)
17 | @driver = driver
18 | @pages = pages
19 | @evidence = Bucky::TestEquipment::Evidence::E2eEvidence.new(driver: driver, test_case: test_case_name)
20 | end
21 |
22 | def pages_getter
23 | @pages
24 | end
25 |
26 | # Check whether title of web page matches expected value
27 | # @param [Hash]
28 | def assert_title(**args)
29 | Bucky::Utils::BuckyLogger.write('assert_title', args)
30 | verify_rescue { assert_equal(args[:expect]&.to_s, @driver.title, 'Not Expected Title.') }
31 | end
32 |
33 | # Check whether text of web element matches expected value
34 | # @param [Hash]
35 | def assert_text(**args)
36 | Bucky::Utils::BuckyLogger.write('assert_text', args)
37 | part = @pages.get_part(args)
38 | verify_rescue { assert_equal(args[:expect]&.to_s, part.text, 'Not Expected Text.') }
39 | end
40 |
41 | # Check whether text of web element contains expected value
42 | # @param [Hash]
43 | def assert_contained_text(**args)
44 | Bucky::Utils::BuckyLogger.write('assert_contained_text', args)
45 | part = @pages.get_part(args)
46 | verify_rescue { assert(part.text.include?(args[:expect]&.to_s), "Not Contain Expected Text.\nexpect: #{args[:expect].to_s.bg_green.black}\nactual: #{part.text.to_s.bg_red.black}") }
47 | end
48 |
49 | # Check whether url contains excepted value
50 | # @param [Hash]
51 | def assert_contained_url(**args)
52 | Bucky::Utils::BuckyLogger.write('assert_contained_url', args)
53 | verify_rescue { assert(@driver.current_url.include?(args[:expect]&.to_s), "Not Contain Expected URL.\nexpect: #{args[:expect].to_s.bg_green.black}\nactual: #{@driver.current_url.to_s.bg_red.black}") }
54 | end
55 |
56 | # Check whether attribute of web element contains excepted value.
57 | # @param [Hash]
58 | def assert_contained_attribute(**args)
59 | Bucky::Utils::BuckyLogger.write('assert_contained_attribute', args)
60 | part = @pages.get_part(args)
61 | verify_rescue { assert(part[args[:attribute]].include?(args[:expect]&.to_s), "Not Contain Expected Attribute.\nexpect: #{args[:expect].to_s.bg_green.black}\nactual: #{part[args[:attribute]].to_s.bg_red.black}") }
62 | end
63 |
64 | # Check whether text of part is number
65 | # @param [Hash]
66 | def assert_is_number(**args)
67 | Bucky::Utils::BuckyLogger.write('assert is number', args)
68 | text = @pages.get_part(args).text.sub(',', '')
69 | verify_rescue { assert(text.to_i.to_s == text.to_s, "Not number.\nactual: #{text.bg_red.black}") }
70 | end
71 |
72 | # Check whether style property includes display:block
73 | # @param [Hash]
74 | def assert_display(**args)
75 | Bucky::Utils::BuckyLogger.write('assert display', args)
76 | verify_rescue { assert_true(@pages.get_part(args).displayed?, "No display this parts.\nURL: #{@driver.current_url}\npage: #{args[:page]}\npart: #{args[:part]}") }
77 | end
78 |
79 | # Check whether web element exists
80 | # @param [Hash]
81 | def assert_exist_part(**args)
82 | Bucky::Utils::BuckyLogger.write('assert_exist_part', args)
83 | verify_rescue { assert_true(@pages.part_exist?(args), "This part is not exist.\nURL: #{@driver.current_url}\npage: #{args[:page]}\npart: #{args[:part]}") }
84 | end
85 |
86 | # Check whether web element don't exist
87 | # @param [Hash]
88 | def assert_not_exist_part(**args)
89 | Bucky::Utils::BuckyLogger.write('assert_not_exist_part', args)
90 | verify_rescue { assert_false(@pages.part_exist?(args), "This part is exist.\nURL: #{@driver.current_url}\npage: #{args[:page]}\npart: #{args[:part]}") }
91 | end
92 |
93 | private
94 |
95 | # Common exception method
96 | # @param [Method] &assert
97 | def verify_rescue
98 | yield
99 | rescue StandardError => e
100 | @evidence.save_evidence(e)
101 | raise e
102 | end
103 | end
104 | end
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/spec/test_equipment/selenium_handler/webdriver_handler_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../../lib/bucky/test_equipment/selenium_handler/webdriver_handler'
4 |
5 | describe Bucky::TestEquipment::SeleniumHandler::WebdriverHandler do
6 | include Bucky::TestEquipment::SeleniumHandler::WebdriverHandler
7 | let(:config_double) { double('double of Config') }
8 | let(:webdriver_double) { double('double of Webdriver') }
9 | let(:webdriver_manage_double) { double('double of Webdriver.manage') }
10 | let(:webdriver_manage_window_double) { double('double of Webdriver.manage.window') }
11 | let(:webdriver_manage_timeouts_double) { double('double of Webdriver.manage.timeouts') }
12 | let(:webdriver_manage_timeouts_double_implicit_wait) { double('double of Webdriver.manage.timeouts.implicit_wait') }
13 |
14 | before do
15 | allow(Bucky::Utils::Config).to receive(:instance).and_return(config_double)
16 | allow(config_double).to receive('[]').with(:browser).and_return(browser)
17 | allow(config_double).to receive('[]').with(:selenium_ip).and_return(selenium_ip)
18 | allow(config_double).to receive('[]').with(:selenium_port).and_return(selenium_port)
19 | allow(config_double).to receive('[]').with(:device_name_on_chrome).and_return(device_name_on_chrome)
20 | allow(config_double).to receive('[]').with(:sp_device_name).and_return(sp_device_name)
21 | allow(config_double).to receive('[]').with(:tablet_device_name).and_return(sp_device_name)
22 | allow(config_double).to receive('[]').with(:bucky_error).and_return(bucky_error)
23 | allow(config_double).to receive('[]').with(:driver_open_timeout).and_return(10)
24 | allow(config_double).to receive('[]').with(:driver_read_timeout).and_return(10)
25 | allow(config_double).to receive('[]').with(:user_agent).and_return(10)
26 | allow(config_double).to receive('[]').with(:find_element_timeout).and_return(10)
27 | allow(config_double).to receive('[]').with(:headless).and_return(headless)
28 | allow(config_double).to receive('[]').with(:chromedriver_flags).and_return(chromedriver_flags)
29 | allow(Selenium::WebDriver).to receive(:for).and_return(webdriver_double)
30 | allow(webdriver_double).to receive(:manage).and_return(webdriver_manage_double)
31 | allow(webdriver_manage_double).to receive(:window).and_return(webdriver_manage_window_double)
32 | allow(webdriver_manage_window_double).to receive(:resize_to)
33 | allow(webdriver_manage_double).to receive(:timeouts).and_return(webdriver_manage_timeouts_double)
34 | allow(webdriver_manage_timeouts_double).to receive(:implicit_wait=)
35 | end
36 |
37 | describe '#create_webdriver' do
38 | let(:selenium_ip) { '11.22.33.44' }
39 | let(:selenium_port) { '4444' }
40 | let(:device_name_on_chrome) { { iphone6: 'Apple iPhone 6' } }
41 | let(:sp_device_name) { :iphone6 }
42 | let(:sp_device_name) { :ipad }
43 | let(:bucky_error) { 'bucky error' }
44 | let(:headless) { false }
45 | let(:chromedriver_flags) { ['--foo', '--bar', '--baz'] }
46 |
47 | context 'pc' do
48 | let(:device_type) { 'pc' }
49 | context 'browser is chrome' do
50 | let(:browser) { :chrome }
51 | it 'call Selenium::WebDriver.for' do
52 | expect(Selenium::WebDriver).to receive(:for)
53 | create_webdriver(device_type)
54 | end
55 | end
56 | context 'browser is not chrome' do
57 | let(:browser) { :firefox }
58 | it 'raise exception' do
59 | expect(Bucky::Core::Exception::BuckyException).to receive(:handle)
60 | create_webdriver(device_type)
61 | end
62 | end
63 | end
64 | context 'sp' do
65 | let(:device_type) { 'sp' }
66 | context 'browser is chrome' do
67 | let(:browser) { :chrome }
68 | it 'call Selenium::WebDriver.for' do
69 | expect(Selenium::WebDriver).to receive(:for)
70 | create_webdriver(device_type)
71 | end
72 | end
73 | context 'browser is not chrome' do
74 | let(:browser) { :firefox }
75 | let(:selenium_mode) { :remote }
76 | it 'raise exception' do
77 | expect(Bucky::Core::Exception::BuckyException).to receive(:handle)
78 | create_webdriver(device_type)
79 | end
80 | end
81 | end
82 | context 'tablet' do
83 | let(:device_type) { 'tablet' }
84 | context 'browser is chrome' do
85 | let(:browser) { :chrome }
86 | it 'call Selenium::WebDriver.for' do
87 | expect(Selenium::WebDriver).to receive(:for)
88 | create_webdriver(device_type)
89 | end
90 | end
91 | context 'browser is not chrome' do
92 | let(:browser) { :firefox }
93 | let(:selenium_mode) { :remote }
94 | it 'raise exception' do
95 | expect(Bucky::Core::Exception::BuckyException).to receive(:handle)
96 | create_webdriver(device_type)
97 | end
98 | end
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/lib/bucky/core/test_core/test_class_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../test_equipment/test_case/e2e_test_case'
4 | require_relative '../../test_equipment/test_case/linkstatus_test_case'
5 |
6 | module Bucky
7 | module Core
8 | module TestCore
9 | # For creating class dynamically.
10 | class TestClasses
11 | end
12 |
13 | module TestClassGeneratorHelper
14 | private
15 |
16 | def add_test_procedure(procedures)
17 | procedures.each.with_index(1) do |procedure, step_number|
18 | procedure[:proc] ||= ''.dup
19 | puts " #{step_number}:#{procedure[:proc]}"
20 | method = procedure[:exec].key?(:operate) ? :operate : :verify
21 | send(method, exec: procedure[:exec], step_number:, proc_name: procedure[:proc])
22 | end
23 | end
24 |
25 | def make_test_method_name(data, test_case, index)
26 | if test_case[:case_name].nil?
27 | return [
28 | 'test',
29 | data[:suite][:service],
30 | data[:suite][:device],
31 | data[:test_category],
32 | data[:test_suite_name],
33 | index.to_s
34 | ].join('_')
35 | end
36 | "test_#{test_case[:case_name]}"
37 | end
38 | end
39 |
40 | class TestClassGenerator
41 | attr_accessor :test_classes
42 |
43 | def initialize(test_cond)
44 | @test_classes = TestClasses
45 | @test_cond = test_cond
46 | end
47 |
48 | # Genrate test class by test suite and test case data
49 | def generate_test_class(data: [], linkstatus_url_log: {}, w_pipe: {})
50 | test_cond = @test_cond
51 | # Common proccessing
52 | # e.g.) TestSampleAppPcE2e1, TestSampleAppPcHttpstatus1
53 | test_class_name = make_test_class_name(data)
54 | # Select super class by test category
55 | super_suite_class = eval format('Bucky::TestEquipment::TestCase::%sTestCase', test_category: data[:test_category].capitalize)
56 | # Define test suite class
57 | test_classes.const_set(test_class_name.to_sym, Class.new(super_suite_class) do |_klass|
58 | extend TestClassGeneratorHelper
59 | include TestClassGeneratorHelper
60 | define_method(:suite_data, proc { data[:suite] })
61 | define_method(:suite_id, proc { data[:test_suite_id] })
62 | define_method(:simple_test_class_name) do |original_name|
63 | match_obj = /\Atest_(.+)\(.+::(Test.+)\)\z/.match(original_name)
64 | "#{match_obj[1]}(#{match_obj[2]})"
65 | end
66 | define_method(:w_pipe, proc { w_pipe })
67 |
68 | # Class structure is different for each test category
69 | case data[:test_category]
70 | when 'linkstatus'
71 | data[:suite][:cases].each_with_index do |t_case, i|
72 | method_name = make_test_method_name(data, t_case, i)
73 | description(
74 | t_case[:case_name],
75 | define_method(method_name) do
76 | puts "\n#{simple_test_class_name(name)}"
77 | t_case[:urls].each do |url|
78 | linkstatus_check_args = { url:, device: data[:suite][:device], exclude_urls: data[:suite][:exclude_urls], link_check_max_times: test_cond[:link_check_max_times], url_log: linkstatus_url_log }
79 | linkstatus_check(linkstatus_check_args)
80 | end
81 | end
82 | )
83 | end
84 |
85 | when 'e2e'
86 | if data[:suite][:setup_each]
87 | def setup
88 | super
89 | puts "[setup]#{simple_test_class_name(name)}"
90 | add_test_procedure(suite_data[:setup_each][:procs])
91 | end
92 | end
93 |
94 | if data[:suite][:teardown_each]
95 | def teardown
96 | puts "[teardown]#{simple_test_class_name(name)}"
97 | add_test_procedure(suite_data[:teardown_each][:procs])
98 | super
99 | end
100 | end
101 |
102 | # Generate test case method
103 | data[:suite][:cases].each_with_index do |t_case, i|
104 | # e.g.) test_sample_app_pc_e2e_1_2
105 | method_name = make_test_method_name(data, t_case, i)
106 | method_obj = proc do
107 | puts "\n#{simple_test_class_name(name)}\n #{t_case[:desc]} ...."
108 | add_test_procedure(t_case[:procs])
109 | end
110 | description(t_case[:case_name], define_method(method_name, method_obj))
111 | end
112 | end
113 | end)
114 | end
115 |
116 | private
117 |
118 | def make_test_class_name(data)
119 | [
120 | 'Test',
121 | data[:suite][:service].split(/_|-/).map(&:capitalize).join.to_s,
122 | data[:suite][:device].capitalize.to_s,
123 | data[:test_category].to_s.capitalize.to_s,
124 | (data[:test_class_name]).to_s
125 | ].join
126 | end
127 | end
128 | end
129 | end
130 | end
131 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | generate_cache_for_system_test:
4 | machine: true
5 | steps:
6 | - checkout
7 | - restore_cache:
8 | key: docker-{{ checksum ".circleci/config.yml" }}-{{ checksum "docker-compose.system-test.yml" }}-{{ checksum "Dockerfile.system-test" }}-{{ checksum "bucky-core.gemspec" }}-{{ checksum ".dockerignore" }}
9 | - run:
10 | command: |
11 | if [ ! -f ~/caches/images.tar ]; then
12 | docker-compose -f docker-compose.system-test.yml build && \
13 | mkdir -p ~/caches && \
14 | docker save $(docker images | awk 'NR>=2 && ! /^/{print $1}') -o ~/caches/images.tar
15 | fi
16 | - save_cache:
17 | key: docker-{{ checksum ".circleci/config.yml" }}-{{ checksum "docker-compose.system-test.yml" }}-{{ checksum "Dockerfile.system-test" }}-{{ checksum "bucky-core.gemspec" }}-{{ checksum ".dockerignore" }}
18 | paths: ~/caches/images.tar
19 | system_test:
20 | machine: true
21 | parallelism: 2
22 | steps:
23 | - checkout
24 | - restore_cache:
25 | key: docker-{{ checksum ".circleci/config.yml" }}-{{ checksum "docker-compose.system-test.yml" }}-{{ checksum "Dockerfile.system-test" }}-{{ checksum "bucky-core.gemspec" }}-{{ checksum ".dockerignore" }}
26 | - run:
27 | command: docker load -q -i ~/caches/images.tar
28 | - run:
29 | name: docker up
30 | command: docker-compose -f docker-compose.system-test.yml up -d
31 | - run:
32 | name: execute system testing
33 | command: 'circleci tests glob system_testing/testing_code/*.bats | xargs -n 1 -I {} docker exec bucky-core bats "/bucky-core/"{} | circleci tests split'
34 | - run:
35 | name: docker down
36 | command: docker-compose -f docker-compose.system-test.yml down
37 | unit_test:
38 | working_directory: ~/bucky-core
39 | docker:
40 | - image: cimg/ruby:3.2.0
41 | environment:
42 | CC_TEST_REPORTER_ID: fd7bd9d517bdf8953c4d4803ca4ad7539d12d5c760048b8daf80cbc7d54fb262
43 | steps:
44 | - checkout
45 | - type: cache-restore
46 | key: unit-test-{{ checksum "Gemfile.lock" }}
47 | - run: bundle install --path vendor/bundle --quiet
48 | - type: cache-save
49 | key: unit-test-{{ checksum "Gemfile.lock" }}
50 | paths:
51 | - vendor/bundle
52 | # Download test-reporter
53 | - run:
54 | name: Setup Code Climate test-reporter
55 | command: |
56 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
57 | chmod +x ./cc-test-reporter
58 | # Run rspec and show on code climate
59 | - run: |
60 | ./cc-test-reporter before-build
61 | bundle exec rspec
62 | ./cc-test-reporter after-build --coverage-input-type simplecov --exit-code $?
63 | static_code_analysis:
64 | docker:
65 | - image: cimg/ruby:3.2.0
66 | steps:
67 | - checkout
68 | - type: cache-restore
69 | key: syntax-check-{{ checksum "Gemfile.lock" }}
70 | - run: bundle install --path vendor/bundle --quiet
71 | - type: cache-save
72 | key: syntax-check-{{ checksum "Gemfile.lock" }}
73 | paths:
74 | - vendor/bundle
75 | - run:
76 | name: Run RuboCop on changed files
77 | command: |
78 | git diff --name-only origin/master...HEAD -- '*.rb' | xargs -r bundle exec rubocop -f html --out report.html
79 | - store_artifacts:
80 | path: report.html
81 | publish_to_rubygems:
82 | docker:
83 | - image: cimg/ruby:3.2.0
84 | steps:
85 | - add_ssh_keys:
86 | finerprints:
87 | - "6a:f3:d3:b5:a5:da:ce:e0:9f:22:f8:4a:2f:51:67:2b"
88 | - checkout
89 | - run:
90 | name: Setup Rubygems
91 | command: bash .circleci/setup_rubygems.sh
92 | - run:
93 | name: Update version.rb and publish on RubyGems
94 | command: bash .circleci/deploy_to_rubygems.sh
95 | publish_to_docker_hub:
96 | machine: true
97 | environment:
98 | DOCKER_REPO: lifullsetg/bucky-core
99 | steps:
100 | - checkout
101 | - run:
102 | name: Build and tagged image
103 | command: |
104 | docker build -t "$DOCKER_REPO":"${CIRCLE_TAG/v/}" .
105 | docker tag "$DOCKER_REPO":"${CIRCLE_TAG/v/}" "$DOCKER_REPO":latest
106 | - run:
107 | name: Login and push image
108 | command: |
109 | docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_TOKEN"
110 | docker push "$DOCKER_REPO":"${CIRCLE_TAG/v/}"
111 | docker push "$DOCKER_REPO":latest
112 | docker logout
113 | workflows:
114 | version: 2
115 | test:
116 | jobs:
117 | - static_code_analysis
118 | - unit_test
119 | - generate_cache_for_system_test
120 | - system_test:
121 | requires:
122 | - generate_cache_for_system_test
123 | deploy:
124 | jobs:
125 | - publish_to_rubygems:
126 | filters:
127 | tags:
128 | only: /^v[0-9]{1,}(\.[0-9]{1,}){2}$/
129 | branches:
130 | ignore: /.*/
131 | - publish_to_docker_hub:
132 | filters:
133 | tags:
134 | only: /^v[0-9]{1,}(\.[0-9]{1,}){2}$/
135 | branches:
136 | ignore: /.*/
137 | requires:
138 | - publish_to_rubygems
139 |
--------------------------------------------------------------------------------
/lib/bucky/core/test_core/test_case_loader.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../database/test_data_operator'
4 | require_relative '../../utils/yaml_load'
5 |
6 | class Hash
7 | def deep_symbolize_keys!
8 | deep_transform_keys! do |key|
9 | key unless key.respond_to? :to_sym
10 | key.to_sym
11 | end
12 | end
13 |
14 | def deep_transform_keys!(&block)
15 | keys.each do |key|
16 | value = delete(key)
17 | self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
18 | end
19 | self
20 | end
21 | end
22 |
23 | module Bucky
24 | module Core
25 | module TestCore
26 | class TestCaseLoader
27 | class << self
28 | include Bucky::Utils::YamlLoad
29 | # Load test code files and return test suite data.
30 | # @return [Array] test suite
31 | def load_testcode(test_cond)
32 | return load_re_test_testcode(test_cond[:re_test_cond]) if test_cond.key? :re_test_cond
33 |
34 | testcodes = []
35 | service = (test_cond[:service] || ['*']).first
36 | device = (test_cond[:device] || ['*']).first
37 |
38 | Dir.glob("#{$bucky_home_dir}/services/#{service}/#{device}/scenarios/#{test_cond[:test_category]}/*.yml").each do |testcode_file|
39 | testcodes << load_testcode_in_file(testcode_file, test_cond)
40 | end
41 | # Delete nil element
42 | testcodes.compact
43 | end
44 |
45 | private
46 |
47 | def load_testcode_in_file(testcode_file, test_cond)
48 | testcode_info = testcode_file.split(%r{/|\.}).reverse
49 | test_suite_name, test_category = testcode_info[1..3]
50 | test_class_name = test_suite_name.split('_').map(&:capitalize).join
51 | test_suite = load_suite_file(testcode_file)
52 | return nil unless target_in_suite_options?(test_suite, test_cond, test_suite_name)
53 |
54 | test_suite = set_suite_option_to_case(test_suite)
55 | refined_suite = refine_case_with_option(test_cond, test_suite)
56 | return nil if refined_suite[:cases].empty?
57 |
58 | { test_class_name: test_class_name, test_suite_name: test_suite_name, test_category: test_category, suite: refined_suite }
59 | end
60 |
61 | def load_re_test_testcode(re_test_cond)
62 | testcodes = []
63 | re_test_cond.each_value do |cond|
64 | testcode_info = cond[:file_path].split(%r{/|\.}).reverse
65 | test_suite_name, test_category = testcode_info[1..3]
66 | test_class_name = test_suite_name.split('_').map(&:capitalize).join
67 | test_suite = load_suite_file(cond[:file_path])
68 | test_suite[:cases].delete_if { |c| !cond[:case_names].include? c[:case_name] }
69 | testcodes << { test_class_name: test_class_name, test_suite_name: test_suite_name, test_category: test_category, suite: test_suite }
70 | end
71 | testcodes
72 | end
73 |
74 | def load_suite_file(file)
75 | test_suite = load_yaml(file)
76 | test_suite.deep_symbolize_keys!
77 | test_suite[:setup_each][:procs].each(&:deep_symbolize_keys!) if test_suite[:setup_each]
78 | test_suite[:teardown_each][:procs].each(&:deep_symbolize_keys!) if test_suite[:teardown_each]
79 | test_suite[:cases].each do |c|
80 | c.deep_symbolize_keys!
81 | next unless c.key? :procs
82 |
83 | c[:procs].each do |p|
84 | c[:procs].delete(p) unless when_match?(p)
85 | p.deep_symbolize_keys!
86 | end
87 | end
88 | test_suite
89 | rescue StandardError => e
90 | raise e, "loading #{file}, #{e.message}"
91 | end
92 |
93 | def when_match?(proc)
94 | return true unless proc.key?('when')
95 | raise StandardError, 'Please input correct condition' unless [true, false].include?(proc['when'])
96 |
97 | proc['when']
98 | end
99 |
100 | # Return true if match suite condition
101 | # Only priority and suite name
102 | def target_in_suite_options?(test_suite, test_cond, test_suite_name)
103 | # priority
104 | return false unless check_option_include_target?(test_cond[:priority], test_suite[:priority])
105 | # suite_name
106 | return false unless check_option_include_target?(test_cond[:suite_name], test_suite_name)
107 |
108 | true
109 | end
110 |
111 | # Return true, if target includes specified option
112 | def check_option_include_target?(option, target)
113 | # If there is no option, return nil.
114 | if option.nil?
115 | nil
116 | else
117 | return false unless option.include?(target)
118 | end
119 | true
120 | end
121 |
122 | # set suite option to case e.g.) suite's labels are inherited by test cases
123 | # only label
124 | def set_suite_option_to_case(test_suite)
125 | return test_suite unless test_suite.key?(:labels)
126 |
127 | # Pattern of value is different depending on how to write (string/array)
128 | # change to array on all pattern
129 | test_suite[:labels] = [test_suite[:labels]].flatten
130 | test_suite[:cases].each do |c|
131 | c[:labels] = if c.key?(:labels)
132 | [c[:labels]].flatten | test_suite[:labels]
133 | else
134 | test_suite[:labels]
135 | end
136 | end
137 | test_suite
138 | end
139 |
140 | # Filter with option
141 | def refine_case_with_option(test_cond, suite)
142 | # Filtering by label
143 | if test_cond.key? :label
144 | # Delete test case that have no label
145 | # Pattern of value is different depending on how to write (string/array)
146 | # Change to array on all pattern
147 | suite[:cases].delete_if { |c| c[:labels].nil? }
148 | suite[:cases].delete_if { |c| !(test_cond[:label].sort - [c[:labels]].flatten.sort).empty? }
149 | end
150 | # Exclude by label
151 | if test_cond.key? :xlabel
152 | # Delete test case that have specify label
153 | suite[:cases].delete_if { |c| (test_cond[:xlabel].sort - [c[:labels]].flatten.sort).empty? }
154 | end
155 | # If there is no option, do nothing.
156 | return suite unless test_cond.key? :case
157 |
158 | # Delete test case doesn't match
159 | suite[:cases].delete_if { |c| !test_cond[:case].include? c[:case_name] }
160 | suite
161 | end
162 | end
163 | end
164 | end
165 | end
166 | end
167 |
--------------------------------------------------------------------------------
/lib/bucky/core/test_core/test_manager.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'json'
4 | require_relative '../test_core/test_case_loader'
5 | require_relative '../../utils/config'
6 | require_relative './test_class_generator'
7 | require_relative '../test_core/exit_handler'
8 |
9 | module Bucky
10 | module Core
11 | module TestCore
12 | module ParallelHelper
13 | parent_pid = Process.pid
14 |
15 | # Terminate parent and child process when getting interrupt signal
16 | Signal.trap('INT') do
17 | Process.kill('TERM', -1 * parent_pid)
18 | end
19 |
20 | private
21 |
22 | def parallel_new_worker_each(data_set, max_processes, &block)
23 | # Max parallel workers number
24 | available_workers = max_processes
25 |
26 | # If child process dead, available workers increase
27 | Signal.trap('CLD') { available_workers += 1 }
28 |
29 | r_pipe, w_pipe = IO.pipe
30 |
31 | data_set.each do |data|
32 | # Wait until worker is available and handle exit code from previous process
33 | unless available_workers.positive?
34 | Process.wait
35 | Bucky::Core::TestCore::ExitHandler.instance.raise unless $CHILD_STATUS.exitstatus.zero?
36 | end
37 | # Workers decrease when start working
38 | available_workers -= 1
39 | fork { block.call(data, w_pipe) }
40 | end
41 | # Handle all exit code in waitall
42 | Process.waitall.each do |child|
43 | Bucky::Core::TestCore::ExitHandler.instance.raise unless child[1].exitstatus.zero?
44 | end
45 |
46 | w_pipe.close
47 | results_set = collect_results_set(r_pipe)
48 | r_pipe.close
49 |
50 | results_set
51 | end
52 |
53 | def parallel_distribute_into_workers(data_set, max_processes, &block)
54 | # Group the data by remainder of index
55 | data_set_grouped = data_set.group_by.with_index { |_elem, index| index % max_processes }
56 | r_pipe, w_pipe = IO.pipe
57 | # Use 'values' method to get only hash's key into an array
58 | data_set_grouped.values.each do |data_for_pre_worker|
59 | # Number of child process is equal to max_processes (or equal to data_set length when data_set length is less than max_processes)
60 | fork do
61 | data_for_pre_worker.each { |data| block.call(data, w_pipe) }
62 | end
63 | end
64 | # Handle all exit code in waitall
65 | Process.waitall.each do |child|
66 | Bucky::Core::TestCore::ExitHandler.instance.raise unless child[1].exitstatus.zero?
67 | end
68 |
69 | w_pipe.close
70 | results_set = collect_results_set(r_pipe)
71 | r_pipe.close
72 |
73 | results_set
74 | end
75 |
76 | def collect_results_set(r_pipe)
77 | results_set = {}
78 | r_pipe.each_line do |line|
79 | r = JSON.parse(line)
80 | results_set[r['test_class_name']] = r
81 | end
82 |
83 | results_set
84 | end
85 | end
86 |
87 | class TestManager
88 | include ParallelHelper
89 |
90 | # Keep test conditions and round number
91 | def initialize(test_cond)
92 | @test_cond = test_cond
93 | @re_test_count = @test_cond[:re_test_count]
94 | @tdo = Bucky::Core::Database::TestDataOperator.new
95 | @start_time = Time.now
96 | $job_id = @tdo.save_job_record_and_get_job_id(@start_time, @test_cond[:command_and_option], @test_cond[:base_fqdn])
97 | @json_report = {
98 | summary: {
99 | cases_count: 0,
100 | success_count: 0,
101 | failure_count: 0,
102 | job_id: $job_id,
103 | test_category: test_cond[:test_category],
104 | device: test_cond[:device],
105 | labels: test_cond[:label],
106 | exclude_labels: test_cond[:xlabel],
107 | rerun_job_id: test_cond[:job],
108 | round_count: 0
109 | }
110 | }
111 | end
112 |
113 | def run
114 | execute_test
115 | end
116 |
117 | # Rerun by job id
118 | def rerun
119 | rerun_job_id = @test_cond[:job]
120 | $round = @tdo.get_last_round_from_job_id(rerun_job_id)
121 | @test_cond[:re_test_cond] = @tdo.get_ng_test_cases_at_last_execution(
122 | is_error: 1, job_id: rerun_job_id, round: $round
123 | )
124 | execute_test
125 | end
126 |
127 | private
128 |
129 | # Load test suite from test code.
130 | def load_test_suites
131 | test_suite_data = Bucky::Core::TestCore::TestCaseLoader.load_testcode(@test_cond)
132 | raise StandardError, "\nThere is no test case!\nPlease check test condition." if test_suite_data.empty?
133 |
134 | @tdo.update_test_suites_data(test_suite_data)
135 | @tdo.add_suite_id_to_loaded_suite_data(test_suite_data)
136 | end
137 |
138 | # Generate and execute test
139 | def do_test_suites(test_suite_data)
140 | # For checking on linkstatus
141 | e2e_parallel_num = Bucky::Utils::Config.instance[:e2e_parallel_num]
142 | linkstatus_parallel_num = Bucky::Utils::Config.instance[:linkstatus_parallel_num]
143 | tcg = Bucky::Core::TestCore::TestClassGenerator.new(@test_cond)
144 | case @test_cond[:test_category]
145 | when 'e2e' then results_set = parallel_new_worker_each(test_suite_data, e2e_parallel_num) { |data, w_pipe| tcg.generate_test_class(data: data, w_pipe: w_pipe) }
146 | when 'linkstatus' then
147 | linkstatus_url_log = {}
148 | results_set = parallel_distribute_into_workers(test_suite_data, linkstatus_parallel_num) { |data, w_pipe| tcg.generate_test_class(data: data, linkstatus_url_log: linkstatus_url_log, w_pipe: w_pipe) }
149 | end
150 |
151 | results_set
152 | end
153 |
154 | def execute_test
155 | all_round_results = []
156 | @re_test_count.times do |i|
157 | Bucky::Core::TestCore::ExitHandler.instance.reset
158 | $round = i + 1
159 | @json_report[:summary][:round_count] = $round
160 | test_suite_data = load_test_suites
161 | all_round_results.append(do_test_suites(test_suite_data))
162 | @test_cond[:re_test_cond] = @tdo.get_ng_test_cases_at_last_execution(
163 | is_error: 1, job_id: $job_id, round: $round
164 | )
165 | break if @test_cond[:re_test_cond].empty?
166 | end
167 |
168 | return unless @test_cond[:out]
169 |
170 | @json_report[:summary][:cases_count] = all_round_results[0].sum { |_case, res| res['cases_count'] }
171 | @json_report[:summary][:failure_count] = all_round_results[-1].sum { |_case, res| res['failure_count'] }
172 | @json_report[:summary][:success_count] = @json_report[:summary][:cases_count] - @json_report[:summary][:failure_count]
173 |
174 | File.open(@test_cond[:out], 'w') do |f|
175 | f.puts(@json_report.to_json)
176 | puts "\nSave report : #{@test_cond[:out]}\n"
177 | end
178 | end
179 | end
180 | end
181 | end
182 | end
183 |
--------------------------------------------------------------------------------
/exe/bucky:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require 'rubygems'
5 | require 'optparse'
6 | require 'color_echo'
7 | require 'fileutils'
8 | require_relative '../lib/bucky/version'
9 | require_relative '../lib/bucky/core/test_core/exit_handler'
10 |
11 | # Color Settings
12 | CE.fg(:cyan)
13 |
14 | NEW_COMMAND = %w[new].freeze
15 | MAKE_SERVICE_COMMAND = %w[make service].freeze
16 | MAKE_PAGE_COMMAND = %w[make page].freeze
17 | RUN_COMMAND = %w[run].freeze
18 | RERUN_COMMAND = %w[rerun].freeze
19 | LINT_COMMAND = %w[lint].freeze
20 |
21 | # ===================================
22 | # Parse options =====================
23 | # ===================================
24 | opts = OptionParser.new
25 | opts.banner = 'Bucky: A test framework that supports Life Cycle of System Testing.'
26 | opts.define_head 'Usage: bucky [task]'
27 | opts.separator ''
28 | opts.separator 'Examples:'
29 | opts.separator " bucky #{RUN_COMMAND.join(' ')} --test_category e2e --suite_name test_suite --case_name test_case_1"
30 | opts.separator " bucky #{RUN_COMMAND.join(' ')} -t e2e -s 1 -c 1"
31 | opts.separator " bucky #{RUN_COMMAND.join(' ')} --test_category linkstatus --re_test_count 3"
32 | opts.separator " bucky #{RUN_COMMAND.join(' ')} --test_category e2e --suite_name test_suite --case_name test_case_1 --device sp --priority high --re_test_count 3"
33 | opts.separator ''
34 | opts.separator 'For more information see https://github.com/lifull-dev/bucky-core'
35 | opts.separator ''
36 | opts.separator 'Options:'
37 |
38 | opts.on_tail('-h', '-?', '--help', 'Show this message') do
39 | puts opts
40 | exit
41 | end
42 |
43 | opts.on_tail('-v', '--version', 'Show version') do
44 | puts "BuckyCore: #{Bucky::Version::VERSION}"
45 | exit
46 | end
47 |
48 | test_cond = {}
49 | # Save command and option
50 | test_cond[:command_and_option] = 'bucky ' + ARGV.join(' ')
51 | opts.on('-t', '--test_category TEST_CATEGORY', 'e.g. --test_category e2e, -t e2e') do |v|
52 | test_cond[:test_category] = v
53 | end
54 | opts.on('-s', '--suite_name SUITE_NAME') do |v|
55 | test_cond[:suite_name] = v
56 | end
57 | # opts.on('-c', '--case_id CASE_ID') do |v|
58 | # test_cond[:case_id] = v
59 | # end
60 | opts.on('-S', '--service SERVICE') do |v|
61 | test_cond[:service] = v
62 | end
63 | opts.on('-D', '--device DEVICE') do |v|
64 | test_cond[:device] = v
65 | end
66 | opts.on('-p', '--priority PRIORITY') do |v|
67 | test_cond[:priority] = v
68 | end
69 | opts.on('-d', 'Not Insert TestReport') do |_|
70 | $debug = true
71 | end
72 |
73 | # TODO: Add error handling, if the argument of '-r' is not greater than 0
74 | opts.on('-r', '--re_test_count RE_TEST_COUNT') do |v|
75 | test_cond[:re_test_count] = v
76 | end
77 | opts.on('-c', '--case CASE_NAME') do |v|
78 | test_cond[:case] = v
79 | end
80 | opts.on('-j', '--job_id JOB_ID') do |v|
81 | test_cond[:job] = v
82 | end
83 | opts.on('-l', '--label LABEL_NAME') do |v|
84 | test_cond[:label] = v
85 | end
86 | opts.on('--xl', '--exclude_label EXCLUDE_LABEL_NAME') do |v|
87 | test_cond[:xlabel] = v
88 | end
89 | opts.on('-m', '--link_check_max_times') do |v|
90 | test_cond[:link_check_max_times] = v.to_i
91 | end
92 | opts.on('-o', '--out JSON_OUTPUT_FILE_PATH') do |v|
93 | test_cond[:out] = v
94 | end
95 | lint_cond = {}
96 | opts.on('-C', '--category CATEGORY_NAME') do |v|
97 | lint_cond[:lint_category] = v
98 | end
99 |
100 | opts.parse!
101 |
102 | # ===================================
103 | # Select by command =================
104 | # ===================================
105 |
106 | # Output error message and exit
107 | # @param [String] msg error message
108 | def error_and_exit(msg = nil)
109 | CE.fg(:yellow)
110 | puts 'Invalid command error.'
111 | CE.fg(:red)
112 | puts " [Error] #{msg}" if msg
113 | CE.fg(:yellow)
114 | puts " your command : bucky #{ARGV.join(' ')}"
115 | puts ' Please input some commands.'
116 | puts " e.g. bucky #{NEW_COMMAND.join(' ')}"
117 | puts " bucky #{RUN_COMMAND.join(' ')}"
118 | puts " bucky #{MAKE_SERVICE_COMMAND.join(' ')}"
119 | puts " bucky #{MAKE_PAGE_COMMAND.join(' ')}"
120 | puts " bucky #{RERUN_COMMAND.join(' ')}"
121 | puts " bucky #{LINT_COMMAND.join(' ')}"
122 | exit
123 | end
124 |
125 | def bucky_home?
126 | File.exist?('.bucky_home')
127 | end
128 |
129 | def setup_test_cond(test_cond)
130 | # Default conditions setting conditions setting
131 | test_cond[:test_category] ||= 'e2e'
132 | test_cond[:re_test_count] = test_cond[:re_test_count] ? test_cond[:re_test_count].to_i : 1
133 | test_cond[:link_check_max_times] ||= 3
134 | # Change to array e.g.--suite_id 1,2,3 -> :suite_id=>[1,2,3]
135 | %i[suite_name case label xlabel device].each do |k|
136 | test_cond[k] = test_cond[k].split(',') unless test_cond[k].nil?
137 | end
138 | test_cond[:base_fqdn] = ENV['BASE_FQDN'] ||= ''
139 | test_cond
140 | end
141 |
142 | current_dir = Dir.pwd
143 | gem_script_dir = __dir__
144 |
145 | if ARGV[0].end_with? 'run'
146 | error_and_exit('Not bucky project dirctory here.') unless bucky_home?
147 | $bucky_home_dir = Dir.pwd
148 | require_relative '../lib/bucky/core/test_core/test_manager'
149 | Bucky::Core::TestCore::TestManager.new(setup_test_cond(test_cond)).send(ARGV[0])
150 | Bucky::Core::TestCore::ExitHandler.instance.bucky_exit
151 | elsif ARGV == LINT_COMMAND
152 | $bucky_home_dir = Dir.pwd
153 | # Default conditions setting
154 | lint_cond[:lint_category] ||= 'config'
155 | require_relative '../lib/bucky/tools/lint'
156 | Bucky::Tools::Lint.check(lint_cond[:lint_category])
157 | elsif ARGV[0..0] == NEW_COMMAND
158 | error_and_exit('No test app name.') if ARGV.length < 2
159 |
160 | # Copy template
161 | FileUtils.cp_r("#{gem_script_dir}/../template/new/", current_dir, verbose: true)
162 | # Rename dir
163 | FileUtils.mv("#{current_dir}/new", "#{current_dir}/#{ARGV[1]}", verbose: true)
164 |
165 | elsif ARGV[0..1] == MAKE_SERVICE_COMMAND
166 | error_and_exit('Not bucky project dirctory here.') unless bucky_home?
167 | error_and_exit('No service name.') if ARGV.length < 3
168 |
169 | service_name = ARGV[2]
170 |
171 | # Check if there is any directory in same service name.
172 | error_and_exit("Already exist #{service_name} directory.") if File.exist?("#{current_dir}/services/#{service_name}")
173 | FileUtils.mkdir("#{current_dir}/services/#{service_name}", verbose: true)
174 |
175 | elsif ARGV[0..1] == MAKE_PAGE_COMMAND
176 | support_device = %w[pc sp tablet]
177 | error_and_exit('Not bucky project dirctory here.') unless bucky_home?
178 | error_and_exit('No page name.') if ARGV.length < 3
179 | error_and_exit('No service name.') unless test_cond[:service]
180 |
181 | # Default conditions setting
182 | test_cond[:device] ||= 'pc'
183 |
184 | error_and_exit("#{test_cond[:device]} device is not supported. (only pc/sp/tablet.)") unless support_device.include?(test_cond[:device])
185 |
186 | page_name = ARGV[2]
187 | pageobject_dir = "#{current_dir}/services/#{test_cond[:service]}/#{test_cond[:device]}/pageobject"
188 | pageobject_rb = "#{pageobject_dir}/#{page_name}.rb"
189 | parts_dir = "#{current_dir}/services/#{test_cond[:service]}/#{test_cond[:device]}/parts"
190 | parts_file = "#{parts_dir}/#{page_name}.yml"
191 |
192 | error_and_exit("Already exist pageobject file: #{pageobject_rb}.") if File.exist?(pageobject_rb)
193 | error_and_exit("Already exist parts file: #{parts_file}.") if File.exist?(parts_file)
194 | FileUtils.mkdir_p("#{current_dir}/services/#{test_cond[:service]}/#{test_cond[:device]}")
195 | FileUtils.cp_r(
196 | "#{gem_script_dir}/../template/make_page/#{test_cond[:device]}",
197 | "#{current_dir}/services/#{test_cond[:service]}",
198 | verbose: true
199 | )
200 |
201 | FileUtils.mv("#{pageobject_dir}/sample_page.rb", pageobject_rb, verbose: true)
202 | FileUtils.mv("#{parts_dir}/sample_page.yml", parts_file, verbose: true)
203 |
204 | # Change service name and page name on pageobject
205 | # | {SampleService} -> ServiceName
206 | # | {SamplePage} -> PageName
207 | script = File.open(pageobject_rb).read
208 | split_and_capitalize = proc { |str| str.split(/_|-/).map(&:capitalize).join }
209 | script.sub!('{SampleService}', split_and_capitalize.call(test_cond[:service]))
210 | script.sub!('{SamplePage}', split_and_capitalize.call(page_name))
211 | File.open(pageobject_rb, 'w+') { |f| f.write script }
212 | else
213 | error_and_exit
214 | end
215 |
--------------------------------------------------------------------------------