-
Notifications
You must be signed in to change notification settings - Fork 365
Cypress on rails #9633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cypress on rails #9633
Changes from all commits
77976ac
4629ebc
661d157
a1054fa
0e9c077
3311ca0
e4d2344
a803026
1191a31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # This is identical to the generated file[1] with the exceptions below: | ||
| # [1] https://github.com/shakacode/cypress-playwright-on-rails/blob/ac5d69f9ea951c7545e5141db2abb2cb1350e740/lib/generators/cypress_on_rails/templates/config/initializers/cypress_on_rails.rb.erb | ||
| # * install_folder (uses our rails engine path) | ||
| # * require ENV['CYPRESS'] to be set | ||
| # * requires and instantiates the seeded_deletion strategy | ||
| if ENV['CYPRESS'].present? | ||
| CypressOnRails.configure do |c| | ||
| c.api_prefix = "" | ||
|
|
||
| # Currently, the only change from the template: | ||
| c.install_folder = ManageIQ::UI::Classic::Engine.root.join("cypress/e2e") | ||
| # WARNING!! CypressOnRails can execute arbitrary ruby code | ||
| # please use with extra caution if enabling on hosted servers or starting your local server on 0.0.0.0 | ||
| c.use_middleware = !Rails.env.production? | ||
| # c.use_vcr_middleware = !Rails.env.production? | ||
| # # Use this if you want to use use_cassette wrapper instead of manual insert/eject | ||
| # # c.use_vcr_use_cassette_middleware = !Rails.env.production? | ||
| # # Pass custom VCR options | ||
| # c.vcr_options = { | ||
| # hook_into: :webmock, | ||
| # default_cassette_options: { record: :once }, | ||
| # cassette_library_dir: File.expand_path("#{__dir__}/../../e2e/cypress/fixtures/vcr_cassettes") | ||
| # } | ||
| c.logger = Rails.logger | ||
|
|
||
| # Server configuration for rake tasks (cypress:open, cypress:run, playwright:open, playwright:run) | ||
| # c.server_host = 'localhost' # or use ENV['CYPRESS_RAILS_HOST'] | ||
| # c.server_port = 3001 # or use ENV['CYPRESS_RAILS_PORT'] | ||
| # c.transactional_server = true # Enable automatic transaction rollback between tests | ||
|
|
||
| # Server lifecycle hooks for rake tasks | ||
| # c.before_server_start = -> { DatabaseCleaner.clean_with(:truncation) } | ||
| # c.after_server_start = -> { puts "Test server started on port #{CypressOnRails.configuration.server_port}" } | ||
| # c.after_transaction_start = -> { Rails.application.load_seed } | ||
| # c.after_state_reset = -> { Rails.cache.clear } | ||
| # c.before_server_stop = -> { puts "Stopping test server..." } | ||
|
|
||
| # If you want to enable a before_request logic, such as authentication, logging, sending metrics, etc. | ||
| # Refer to https://www.rubydoc.info/gems/rack/Rack/Request for the `request` argument. | ||
| # Return nil to continue through the Cypress command. Return a response [status, header, body] to halt. | ||
| # c.before_request = lambda { |request| | ||
| # unless request.env['warden'].authenticate(:secret_key) | ||
| # return [403, {}, ["forbidden"]] | ||
| # end | ||
| # } | ||
| end | ||
|
|
||
| # # if you compile your assets on CI | ||
| # if ENV['CYPRESS'].present? && ENV['CI'].present? | ||
| # Rails.application.configure do | ||
| # config.assets.compile = false | ||
| # config.assets.unknown_asset_fallback = false | ||
| # end | ||
| # end | ||
| require 'extensions/database_cleaner-activerecord-seeded_deletion' | ||
| DatabaseCleaner[:active_record].strategy = DatabaseCleaner::ActiveRecord::SeededDeletion.new(:pre_count => true) | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| logger.debug "[Cypress] Running db_state #{command_options}" | ||
| option = command_options.to_s.downcase | ||
| case option | ||
| when "capture" | ||
| logger.info "[Cypress] DatabaseCleaner capturing" | ||
| DatabaseCleaner.start | ||
| when "restore" | ||
| logger.info "[Cypress] DatabaseCleaner restoring" | ||
| DatabaseCleaner.clean | ||
Fryguy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| CypressOnRails::SmartFactoryWrapper.reload | ||
|
|
||
| if defined?(VCR) | ||
| VCR.eject_cassette # make sure we no cassette inserted before the next test starts | ||
| VCR.turn_off! | ||
| WebMock.disable! if defined?(WebMock) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, I was going to put this into a class/module but it's evaluated in the context of the cypress on rails command executor so finding/prepending classes was more effort than needed. If we want to move the logic into methods, we can do that later. |
||
| end | ||
| else | ||
| message = "Unknown db_state #{option}!" | ||
| logger.error "[Cypress] #{message}" | ||
| raise message | ||
| end | ||
| # Explicitly return nil from the script so the cypress on rails middleware doesn't try to parse a response | ||
| nil | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Kernel.eval(command_options) unless command_options.nil? | ||
Fryguy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| Array.wrap(command_options).map do |factory_options| | ||
| factory_method = factory_options.shift | ||
| begin | ||
| logger.debug "running #{factory_method}, #{factory_options}" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for followup... I should use the same prefix for all logger messages such as here and below... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, this comes from the generator so we'll have to change that generated file if they make changes to it in the future. |
||
| CypressOnRails::SmartFactoryWrapper.public_send(factory_method, *factory_options) | ||
| rescue => e | ||
| logger.error "#{e.class}: #{e.message}" | ||
| logger.error e.backtrace.join("\n") | ||
| logger.error "#{e.record.inspect}" if e.is_a?(ActiveRecord::RecordInvalid) | ||
| raise e | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # This is loaded once before the first command is executed | ||
|
|
||
| begin | ||
| require 'database_cleaner-active_record' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, this file is loaded differently as it's run through their executor thing. We might be able to drop many of the requires here since it's required in engine.rb. But for now, I kept it mostly like they have it in their generator. |
||
| rescue LoadError => e | ||
| puts e.message | ||
| begin | ||
| require 'database_cleaner' | ||
| rescue LoadError => e | ||
| puts e.message | ||
| end | ||
| end | ||
|
|
||
| begin | ||
| require 'factory_bot_rails' | ||
| rescue LoadError => e | ||
| puts e.message | ||
| begin | ||
| require 'factory_girl_rails' | ||
| rescue LoadError => e | ||
| puts e.message | ||
| end | ||
| end | ||
|
|
||
| require 'cypress_on_rails/smart_factory_wrapper' | ||
|
|
||
| factory = CypressOnRails::SimpleRailsFactory | ||
| factory = FactoryBot if defined?(FactoryBot) | ||
| factory = FactoryGirl if defined?(FactoryGirl) | ||
|
|
||
| # TODO: By default, Factory bot sets definition_file_paths to directories to search for factories: | ||
| # https://github.com/thoughtbot/factory_bot/blob/8446cb6c5b39ea046d8ba180197aabc66adf62ed/lib/factory_bot/find_definitions.rb#L10 | ||
| # Cypress on rails SmartFactoryWrapper is expecting files to be a pattern or a file that when evaluated with Dir[xxx], | ||
| # will return files, not a directory: | ||
| # https://github.com/shakacode/cypress-playwright-on-rails/blob/abb505c0691c29d5f2a57c2ba28aedbfd43d079e/lib/cypress_on_rails/smart_factory_wrapper.rb#L88 | ||
| # Therefore, to reuse the existing definition_file_paths, we must convert the directories to glob pattern matches. | ||
| require Rails.root.join('spec/support/factory_bot_helper') | ||
| CypressOnRails::SmartFactoryWrapper.configure( | ||
| always_reload: false, | ||
| factory: factory, | ||
| files: FactoryBot.definition_file_paths.flat_map { |p| p.directory? ? p.join("**/*.rb") : p } | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,23 @@ | ||
| /* eslint-disable no-undef */ | ||
|
|
||
| describe('Automation > Embedded Automate > Explorer', () => { | ||
| before(() => { | ||
| // Create a Domain and Namespace before all the tests | ||
| beforeEach(() => { | ||
| cy.appFactories([ | ||
| ['create', 'miq_ae_domain', {name: 'TestDomain'}], | ||
| ]).then((results) => { | ||
| cy.appFactories([ | ||
| ['create', 'miq_ae_namespace', {name: 'TestNameSpace', domain_id: results[0].id}] | ||
| ]) | ||
| }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is how you can use factories in cypress to create the domain and namespace and replaces all of that code on the left hand side. If you're testing the creation in the UI, the code on the left is fine but since this is just setup code, the factories above should be used. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, this |
||
|
|
||
| cy.login(); | ||
| cy.intercept('POST', '/ops/accordion_select?id=rbac_accord').as('accordion'); | ||
| cy.menu('Automation', 'Embedded Automate', 'Explorer'); | ||
| cy.get('#explorer_title_text'); | ||
|
|
||
| // Creates a Domain | ||
| cy.get('[title="Datastore"]').click(); | ||
| cy.get('[title="Configuration"]').click(); | ||
| cy.get('[title="Add a New Domain"]').click(); | ||
| cy.get('[name="name"]').type('TestDomain', {force: true}); | ||
| cy.get('[name="description"]').type('This is a test domain'); | ||
| cy.get('#enabled').check(); | ||
| cy.get('[class="bx--btn bx--btn--primary"]').contains('Add').click(); | ||
|
|
||
| // Check for the success message | ||
| cy.get('div.alert.alert-success.alert-dismissable').should('exist').and('contain', 'Automate Domain "TestDomain" was added').find('button.close').should('exist'); | ||
|
|
||
| // Creates a Namespace | ||
| cy.get('[title="Datastore"]').click(); | ||
| cy.get('[title="Automate Domain: TestDomain"]').click(); // Click on Domain | ||
| cy.get('[title="Configuration"]').click(); | ||
| cy.get('[title="Add a New Namespace"]').click(); | ||
| cy.get('[name="name"]').type('TestNameSpace', {force: true}); | ||
| cy.get('[name="description"]').type('This is a test NameSpace'); | ||
| cy.get('.bx--btn--primary').contains('Add').click(); | ||
|
|
||
| // Wait for namespace to be visible | ||
| cy.get('[title="Automate Namespace: TestNameSpace"]', {timeout: 1000}).should('be.visible') | ||
| }); | ||
|
|
||
| beforeEach(() => { | ||
| cy.login(); | ||
| cy.intercept('POST', '/ops/accordion_select?id=rbac_accord').as('accordion'); | ||
| cy.menu('Automation', 'Embedded Automate', 'Explorer'); | ||
| cy.get('#explorer_title_text'); | ||
| afterEach(() => { | ||
| cy.appDbState('restore'); | ||
| }); | ||
|
|
||
| describe('Class Form', () => { | ||
|
|
@@ -268,15 +248,4 @@ describe('Automation > Embedded Automate > Explorer', () => { | |
| cy.get('#ns_details_div > .alert').contains('The selected Namespace is empty'); | ||
| }); | ||
| }); | ||
|
|
||
| after(() => { | ||
| // Remove the Domain after all the tests | ||
| cy.menu('Automation', 'Embedded Automate', 'Explorer'); | ||
| cy.get('[title="Datastore"]').click({force: true}); | ||
| cy.get('[title="Automate Domain: TestDomain"]').click({force: true}); | ||
| cy.get('[title="Configuration"]').click({force: true}); | ||
| cy.get('[title="Remove this Domain"]').click({force: true}); | ||
|
|
||
| cy.get('.bx--data-table-content tbody tr').should('not.contain', 'Automate Domain: TestDomain'); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -122,22 +122,23 @@ function createNamespaceAndOpenEditForm() { | |
|
|
||
| describe('Automate operations on Namespaces: Automation -> Embedded Automate -> Explorer -> {Any-created-domain} -> Namespace form', () => { | ||
| beforeEach(() => { | ||
| cy.appFactories([ | ||
| ['create', 'miq_ae_domain', {name: DOMAIN_NAME}], | ||
| ]); | ||
|
|
||
| cy.login(); | ||
| cy.menu( | ||
| AUTOMATION_MENU_OPTION, | ||
| EMBEDDED_AUTOMATION_MENU_OPTION, | ||
| EXPLORER_MENU_OPTION | ||
| ); | ||
| cy.accordion(DATA_STORE_ACCORDION_LABEL); | ||
| /* TODO: DATA_SETUP - Refactor to use API for domain data setup */ | ||
| cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_ADD_NEW_DOMAIN); | ||
| addDomainOrNamespace({ | ||
| nameFieldValue: DOMAIN_NAME, | ||
| }); | ||
| cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_ADD_SUCCESS); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the setup code that navigated to the add domain screen to create it and instead used factories. I kept the other setup navigation steps. |
||
| cy.selectAccordionItem([DATA_STORE_ACCORDION_LABEL, DOMAIN_NAME]); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| cy.appDbState('restore'); | ||
| }); | ||
|
|
||
| it('Validate Add Namespace form fields', () => { | ||
| cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_ADD_NEW_NAMESPACE); | ||
| validateNamespaceFormFields(); | ||
|
|
@@ -245,34 +246,4 @@ describe('Automate operations on Namespaces: Automation -> Embedded Automate -> | |
| }).click(); | ||
| cy.expect_flash(flashClassMap.warning, FLASH_MESSAGE_CANCELLED); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| cy.url() | ||
| .then((url) => { | ||
| // Ensures navigation to Automation -> Embedded Automate -> Explorer in the UI | ||
| if (!url.endsWith(COMPONENT_ROUTE_URL)) { | ||
| cy.visit(COMPONENT_ROUTE_URL); | ||
| } | ||
| cy.accordion(DATA_STORE_ACCORDION_LABEL); | ||
| }) | ||
| .then(() => { | ||
| cy.get('div.panel-collapse.collapse.in li.list-group-item').each( | ||
| (item) => { | ||
| const text = item.text().trim(); | ||
| if (text === DOMAIN_NAME) { | ||
| if (!item.hasClass('node-selected')) { | ||
| cy.wrap(item).click(); | ||
| } | ||
| cy.expect_browser_confirm_with_text({ | ||
| confirmTriggerFn: () => | ||
| cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_REMOVE_DOMAIN), | ||
| containsText: BROWSER_CONFIRM_REMOVE_MESSAGE, | ||
| }); | ||
| return false; // exit iteration | ||
| } | ||
| return null; // has no impact, just to get rid of eslint warning | ||
| } | ||
| ); | ||
| }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -534,36 +534,6 @@ describe('Automate Schedule form operations: Settings > Application Settings > S | |
| }); | ||
|
|
||
| afterEach(() => { | ||
| cy.url() | ||
| .then((url) => { | ||
| // Ensures navigation to Settings -> Application-Settings in the UI | ||
| if (!url.endsWith(COMPONENT_ROUTE_URL)) { | ||
| cy.visit(COMPONENT_ROUTE_URL); | ||
| } | ||
| cy.accordion(SETTINGS_OPTION); | ||
| }) | ||
| .then(() => { | ||
| // Iterate and clean up any leftover schedules created during the test | ||
| cy.get('div.panel-collapse.collapse.in li.list-group-item').each( | ||
| (item) => { | ||
| const text = item.text().trim(); | ||
| if ( | ||
| text === INITIAL_SCHEDULE_NAME || | ||
| text === EDITED_SCHEDULE_NAME | ||
| ) { | ||
| if (!item.hasClass('node-selected')) { | ||
| cy.wrap(item).click(); | ||
| } | ||
| cy.expect_browser_confirm_with_text({ | ||
| confirmTriggerFn: () => | ||
| selectConfigMenu(DELETE_SCHEDULE_CONFIG_OPTION), | ||
| containsText: BROWSER_ALERT_DELETE_CONFIRM_TEXT, | ||
| }); | ||
| return false; // exit iteration | ||
| } | ||
| return null; // has no impact, just to get rid of eslint warning | ||
| } | ||
| ); | ||
| }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🥳 |
||
| cy.appDbState('restore'); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.