From 957ab658a0362a405b124809e1b8505921aaaddb Mon Sep 17 00:00:00 2001 From: Samir Jha Date: Fri, 8 Oct 2021 15:19:55 +0000 Subject: [PATCH] Fixes #33720 - Move collections to generic UI --- app/services/katello/repository_type.rb | 7 +- .../katello/repository_type_manager.rb | 8 ++ config/routes.rb | 6 +- .../repositories/repositories.routes.js | 14 ++-- .../repository_types/ansible_collection.rb | 3 +- webpack/components/RoutedTabs/index.js | 2 +- webpack/containers/Application/config.js | 10 +-- .../AnsibleCollectionsTableSchema.js | 2 +- webpack/scenes/Content/ContentConfig.js | 67 ++++++++++++++++- webpack/scenes/Content/ContentPage.js | 7 +- .../scenes/Content/Details/ContentDetails.js | 2 +- .../ansibleCollectionDetails.fixtures.json | 20 +++++ ...eCollectionRepositoryDetails.fixtures.json | 71 ++++++++++++++++++ .../Details/__tests__/contentDetail.test.js | 75 +++++++++++++++++-- webpack/scenes/Content/Table/ContentTable.js | 10 ++- .../ansibleCollections.fixtures.json | 50 +++++++++++++ .../Content/__tests__/contentTable.test.js | 39 +++++++++- .../__tests__/contentTypes.fixtures.json | 2 +- 18 files changed, 361 insertions(+), 34 deletions(-) create mode 100644 webpack/scenes/Content/Details/__tests__/ansibleCollectionDetails.fixtures.json create mode 100644 webpack/scenes/Content/Details/__tests__/ansibleCollectionRepositoryDetails.fixtures.json create mode 100644 webpack/scenes/Content/__tests__/ansibleCollections.fixtures.json diff --git a/app/services/katello/repository_type.rb b/app/services/katello/repository_type.rb index 9511cb69438..5792a715dbc 100644 --- a/app/services/katello/repository_type.rb +++ b/app/services/katello/repository_type.rb @@ -76,7 +76,7 @@ def default_managed_content_type(label = nil) def content_type(model_class, options = {}) @content_types ||= [] - @content_types << ContentType.new(options.merge(:model_class => model_class)) + @content_types << ContentType.new(options.merge(:model_class => model_class, :content_type => model_class::CONTENT_TYPE)) end def generic_content_type(content_type, options = {}) @@ -127,10 +127,11 @@ def pulp3_api(smart_proxy) class ContentType attr_accessor :model_class, :priority, :pulp2_service_class, :pulp3_service_class, :index, :uploadable, :removable, - :primary_content, :index_on_pulp3, :generic_browser + :primary_content, :index_on_pulp3, :generic_browser, :content_type def initialize(options) self.model_class = options[:model_class] + self.content_type = options[:content_type] self.priority = options[:priority] || 99 self.pulp2_service_class = options[:pulp2_service_class] self.pulp3_service_class = options[:pulp3_service_class] @@ -157,7 +158,7 @@ def as_json(_options = {}) end class GenericContentType < ContentType - attr_accessor :pulp3_api, :pulp3_model, :content_type, :filename_key, :duplicates_allowed, :pluralized_name, + attr_accessor :pulp3_api, :pulp3_model, :filename_key, :duplicates_allowed, :pluralized_name, :model_name, :model_version, :model_filename def initialize(options) diff --git a/app/services/katello/repository_type_manager.rb b/app/services/katello/repository_type_manager.rb index c4b0e6d0dab..d79b4e750f5 100644 --- a/app/services/katello/repository_type_manager.rb +++ b/app/services/katello/repository_type_manager.rb @@ -87,6 +87,14 @@ def generic_content_types(enabled_only = true) list.flatten.map(&:content_type) end + def generic_ui_content_types(enabled_only = true) + repo_types = enabled_only ? enabled_repository_types : defined_repository_types + list = repo_types.values.map do |type| + type.content_types.select { |ct| ct.generic_browser } + end + list.flatten.map(&:content_type) + end + def generic_content_type?(content_type, enabled_only = true) types = generic_content_types(enabled_only) types.include?(content_type) diff --git a/config/routes.rb b/config/routes.rb index 53f5c5d34c1..ee444deb9f7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,15 +24,15 @@ match '/module_streams' => 'react#index', :via => [:get] match '/module_streams/*page' => 'react#index', :via => [:get] - match '/ansible_collections' => 'react#index', :via => [:get] - match '/ansible_collections/*page' => 'react#index', :via => [:get] + match '/legacy_ansible_collections' => 'react#index', :via => [:get] + match '/legacy_ansible_collections/*page' => 'react#index', :via => [:get] match '/content_views' => 'react#index', :via => [:get] match '/content_views/*page' => 'react#index', :via => [:get] match '/content' => 'react#index', :via => [:get] - Katello::RepositoryTypeManager.generic_content_types.each do |type| + Katello::RepositoryTypeManager.generic_ui_content_types.each do |type| get "/#{type.pluralize}", to: redirect("/content/#{type.pluralize}") get "/#{type.pluralize}/:page", to: redirect("/content/#{type.pluralize}/%{page}") match "/content/#{type.pluralize}" => 'react#index', :via => [:get] diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/repositories.routes.js b/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/repositories.routes.js index 0c5604f79d3..e73e80cd9c6 100644 --- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/repositories.routes.js +++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/repositories.routes.js @@ -91,7 +91,7 @@ } }) .state('product.repository.manage-content.docker-manifests', { - url: '/content/docker_manifests', + url: '/docker_manifests', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-docker-manifests.html', ncyBreadcrumb: { @@ -100,7 +100,7 @@ } }) .state('product.repository.manage-content.docker-tags', { - url: '/content/docker_tags', + url: '/docker_tags', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-docker-tags.html', ncyBreadcrumb: { @@ -109,7 +109,7 @@ } }) .state('product.repository.manage-content.docker-manifest-lists', { - url: '/content/docker_manifest_lists', + url: '/docker_manifest_lists', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-docker-manifest-lists.html', ncyBreadcrumb: { @@ -118,7 +118,7 @@ } }) .state('product.repository.manage-content.files', { - url: '/content/files', + url: '/files', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-files.html', ncyBreadcrumb: { @@ -127,7 +127,7 @@ } }) .state('product.repository.manage-content.ostree-branches', { - url: '/content/ostree_branches', + url: '/ostree_branches', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-ostree-branches.html', ncyBreadcrumb: { @@ -145,7 +145,7 @@ } }) .state('product.repository.manage-content.debs', { - url: '/content/debs', + url: '/debs', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-debs.html', ncyBreadcrumb: { @@ -154,7 +154,7 @@ } }) .state('product.repository.manage-content.ansible-collections', { - url: '/content/ansible_collections', + url: '/ansible_collections', permission: 'view_products', templateUrl: 'products/details/repositories/details/views/repository-manage-ansible-collections.html', ncyBreadcrumb: { diff --git a/lib/katello/repository_types/ansible_collection.rb b/lib/katello/repository_types/ansible_collection.rb index c96a7053f80..1e8a58aaa67 100644 --- a/lib/katello/repository_types/ansible_collection.rb +++ b/lib/katello/repository_types/ansible_collection.rb @@ -16,5 +16,6 @@ distribution_class PulpAnsibleClient::AnsibleAnsibleDistribution repo_sync_url_class PulpAnsibleClient::AnsibleRepositorySyncURL - content_type Katello::AnsibleCollection, :pulp3_service_class => ::Katello::Pulp3::AnsibleCollection, :user_removable => true + content_type Katello::AnsibleCollection, :pulp3_service_class => ::Katello::Pulp3::AnsibleCollection, :user_removable => true, generic_browser: true + default_managed_content_type :ansible_collections end diff --git a/webpack/components/RoutedTabs/index.js b/webpack/components/RoutedTabs/index.js index 3e2469a72ce..744c9910a93 100644 --- a/webpack/components/RoutedTabs/index.js +++ b/webpack/components/RoutedTabs/index.js @@ -45,7 +45,7 @@ const RoutedTabs = ({ {content} ))} - + diff --git a/webpack/containers/Application/config.js b/webpack/containers/Application/config.js index 1248fcc758f..206b273b923 100644 --- a/webpack/containers/Application/config.js +++ b/webpack/containers/Application/config.js @@ -47,11 +47,11 @@ export const links = [ component: WithOrganization(withHeader(ModuleStreamDetails, { title: __('Module Stream Details') })), }, { - path: 'ansible_collections', + path: 'legacy_ansible_collections', component: WithOrganization(withHeader(AnsibleCollections, { title: __('Ansible Collections') })), }, { - path: 'ansible_collections/:id([0-9]+)', + path: 'legacy_ansible_collections/:id([0-9]+)', component: WithOrganization(withHeader(AnsibleCollectionDetails, { title: __('Ansible Collection Details') })), }, { @@ -65,14 +65,14 @@ export const links = [ }, { path: 'content', - component: WithOrganization(withHeader(Content, { title: __('Other Content Types') })), + component: WithOrganization(withHeader(Content, { title: __('Content') })), }, { path: 'content/:content_type([a-z_]+)', - component: WithOrganization(withHeader(Content, { title: __('Other Content Types') })), + component: WithOrganization(withHeader(Content, { title: __('Content') })), }, { path: 'content/:content_type([a-z_]+)/:id([0-9]+)', - component: WithOrganization(withHeader(ContentDetails, { title: __('Other Content Type Details') })), + component: WithOrganization(withHeader(ContentDetails, { title: __('Content Details') })), }, ]; diff --git a/webpack/scenes/AnsibleCollections/AnsibleCollectionsTableSchema.js b/webpack/scenes/AnsibleCollections/AnsibleCollectionsTableSchema.js index df2f20be91a..d02a521c692 100644 --- a/webpack/scenes/AnsibleCollections/AnsibleCollectionsTableSchema.js +++ b/webpack/scenes/AnsibleCollections/AnsibleCollectionsTableSchema.js @@ -18,7 +18,7 @@ const TableSchema = [ formatters: [ (value, { rowData }) => ( - {rowData.name} + {rowData.name} ), ], diff --git a/webpack/scenes/Content/ContentConfig.js b/webpack/scenes/Content/ContentConfig.js index f7705b32cd8..373dbddb644 100644 --- a/webpack/scenes/Content/ContentConfig.js +++ b/webpack/scenes/Content/ContentConfig.js @@ -47,7 +47,72 @@ export default () => [ { title: __('Product'), getProperty: unit => - {unit?.product.name}, + {unit?.product.name}, + }, + { + title: __('Sync Status'), + getProperty: unit => + , + }, + { + title: __('Content Count'), + getProperty: (unit, singularLabel) => + (), + }, + ], + }, + ], + }, + { + names: { + pluralTitle: __('Ansible Collections'), + singularTitle: __('Ansible Collection'), + pluralLowercase: __('Ansible collections'), + singularLowercase: __('Ansible collection'), + pluralLabel: 'ansible_collections', + singularLabel: 'ansible_collection', + }, + columnHeaders: [ + { title: __('Name'), getProperty: unit => ({unit?.name}) }, + { title: __('Author'), getProperty: unit => unit?.namespace }, + { title: __('Version'), getProperty: unit => unit?.version }, + { title: __('Checksum'), getProperty: unit => unit?.checksum }, + + ], + tabs: [ + { + tabKey: 'details', + title: __('Details'), + getContent: (contentType, id, tabKey) => , + columnHeaders: [ + { title: __('Name'), getProperty: unit => unit?.name }, + { title: __('Description'), getProperty: unit => unit?.description }, + { title: __('Author'), getProperty: unit => unit?.namespace }, + { title: __('Version'), getProperty: unit => unit?.version }, + { title: __('Checksum'), getProperty: unit => unit?.checksum }, + { title: __('Tags'), getProperty: unit => unit?.tags?.join() }, + ], + }, + { + tabKey: 'repositories', + title: __('Repositories'), + getContent: (contentType, id, tabKey) => + , + columnHeaders: [ + { + title: __('Name'), + getProperty: unit => + {unit?.name}, + }, + { + title: __('Product'), + getProperty: unit => + {unit?.product.name}, }, { title: __('Sync Status'), diff --git a/webpack/scenes/Content/ContentPage.js b/webpack/scenes/Content/ContentPage.js index 5d126df8fa6..f6d04fea9dd 100644 --- a/webpack/scenes/Content/ContentPage.js +++ b/webpack/scenes/Content/ContentPage.js @@ -17,6 +17,7 @@ const ContentPage = () => { const contentTypesResponse = useSelector(selectContentTypes); const contentTypesStatus = useSelector(selectContentTypesStatus); const [selectedContentType, setSelectedContentType] = useState(null); + const [showContentTypeSelector, setShowContentTypeSelector] = useState(true); const [contentTypes, setContentTypes] = useState(null); const { content_type: contentType } = useParams(); @@ -45,6 +46,7 @@ const ContentPage = () => { Object.entries(enabledContentTypes).forEach(([key, value]) => { if (value.includes(contentType)) { setSelectedContentType(key); + setShowContentTypeSelector(false); } }); } @@ -75,7 +77,10 @@ const ContentPage = () => { - + ); diff --git a/webpack/scenes/Content/Details/ContentDetails.js b/webpack/scenes/Content/Details/ContentDetails.js index 14061ab9848..0936a7f43c3 100644 --- a/webpack/scenes/Content/Details/ContentDetails.js +++ b/webpack/scenes/Content/Details/ContentDetails.js @@ -58,7 +58,7 @@ const ContentDetails = () => { - + ); diff --git a/webpack/scenes/Content/Details/__tests__/ansibleCollectionDetails.fixtures.json b/webpack/scenes/Content/Details/__tests__/ansibleCollectionDetails.fixtures.json new file mode 100644 index 00000000000..3e079f5692e --- /dev/null +++ b/webpack/scenes/Content/Details/__tests__/ansibleCollectionDetails.fixtures.json @@ -0,0 +1,20 @@ +{ + "id": 51, + "name": "linux_system_roles", + "namespace": "fedora", + "version": "1.5.0", + "checksum": "edd27bbc1f6ba805760bf85a05efacca989492570e41d263fd804358ce9cef92", + "description": "Ansible roles for linux system components management", + "tags": [ + "collection", + "system" + ], + "repositories": [ + { + "id": 698, + "name": "lin_collections", + "product_id": 296, + "product_name": "test_all_types" + } + ] +} \ No newline at end of file diff --git a/webpack/scenes/Content/Details/__tests__/ansibleCollectionRepositoryDetails.fixtures.json b/webpack/scenes/Content/Details/__tests__/ansibleCollectionRepositoryDetails.fixtures.json new file mode 100644 index 00000000000..d2bfd9d58c5 --- /dev/null +++ b/webpack/scenes/Content/Details/__tests__/ansibleCollectionRepositoryDetails.fixtures.json @@ -0,0 +1,71 @@ +{ + "total": 1, + "subtotal": 1, + "page": "1", + "per_page": "20", + "error": null, + "search": null, + "sort": { + "by": "name", + "order": "asc" + }, + "results": [ + { + "backend_identifier": "b7e662ed-fd91-44ed-94c0-60fc9c5b2ccc", + "relative_path": "Default_Organization/Library/custom/test_all_types/lin_collections", + "container_repository_name": null, + "full_path": "https://centos7-devel4.samir.example.com/pulp_ansible/galaxy/Default_Organization/Library/custom/test_all_types/lin_collections/api/", + "library_instance_id": null, + "version_href": "/pulp/api/v3/repositories/ansible/ansible/59832f57-b06d-49aa-ab3e-ac07c847fb55/versions/1/", + "remote_href": "/pulp/api/v3/remotes/ansible/collection/66badf55-97c1-42be-9e55-c30fdfdd182c/", + "publication_href": null, + "content_counts": { + "ansible_collection": 17 + }, + "id": 698, + "name": "lin_collections", + "label": "lin_collections", + "description": null, + "last_sync": { + "id": "5b6b544a-767f-4558-926d-4c7c828afccc", + "username": "admin", + "started_at": "2021-10-15 11:34:51 -0400", + "ended_at": "2021-10-15 11:35:10 -0400", + "state": "stopped", + "result": "success", + "progress": 1 + }, + "content_view": { + "id": 1, + "name": "Default Organization View" + }, + "content_view_version": { + "id": 1, + "name": "Default Organization View 1.0", + "content_view_id": 1 + }, + "kt_environment": { + "id": 1, + "name": "Library" + }, + "content_type": "ansible_collection", + "url": "http://galaxy.ansible.com/", + "arch": "noarch", + "os_versions": null, + "content_id": "1634312081893", + "generic_remote_options": null, + "major": null, + "minor": null, + "product": { + "id": 296, + "cp_id": "292121829980", + "name": "test_all_types", + "orphaned": false, + "redhat": false, + "sync_plan": null + }, + "content_label": "Default_Organization_test_all_types_lin_collections", + "last_sync_words": "12 days" + } + ] +} \ No newline at end of file diff --git a/webpack/scenes/Content/Details/__tests__/contentDetail.test.js b/webpack/scenes/Content/Details/__tests__/contentDetail.test.js index 9f95eeb7204..eab3c64a704 100644 --- a/webpack/scenes/Content/Details/__tests__/contentDetail.test.js +++ b/webpack/scenes/Content/Details/__tests__/contentDetail.test.js @@ -6,12 +6,14 @@ import api from '../../../../services/api'; import { CONTENT_ID_KEY } from '../../ContentConstants'; import ContentDetails from '../ContentDetails'; import ContentRepositories from '../ContentRepositories'; - -const pythonPackageDetailsResponse = require('./pythonPackageDetails.fixtures.json'); -const pythonPackageRepositoryDetailsResponse = require('./pythonPackageRepositoryDetails.fixtures.json'); +import pythonPackageDetailsResponse from './pythonPackageDetails.fixtures.json'; +import pythonPackageRepositoryDetailsResponse from './pythonPackageRepositoryDetails.fixtures.json'; +import ansibleCollectionDetailsResponse from './ansibleCollectionDetails.fixtures.json'; +import ansibleCollectionRepositoryDetailsResponse from './ansibleCollectionRepositoryDetails.fixtures.json'; const pythonPackageDetailsPath = api.getApiUrl('/python_packages/1491'); -const pythonPackageRepositoryDetailsPath = api.getApiUrl('/repositories'); +const ansibleCollectionDetailsPath = api.getApiUrl('/ansible_collections/51'); +const contentRepositoryDetailsPath = api.getApiUrl('/repositories'); const withContentRoute = component => {component}; @@ -60,7 +62,7 @@ test('Can call API for Python package repository details and show repositories t const contentCountWords = '1489 Python packages'; const pythonPackageRepositoryDetailsScope = nockInstance - .get(pythonPackageRepositoryDetailsPath) + .get(contentRepositoryDetailsPath) .query(true) .reply(200, pythonPackageRepositoryDetailsResponse); @@ -80,3 +82,66 @@ test('Can call API for Python package repository details and show repositories t assertNockRequest(autocompleteScope); assertNockRequest(pythonPackageRepositoryDetailsScope, done); }); + +test('Can call API for Ansible collection details and show details tab on page load', async (done) => { + const renderOptions = { + apiNamespace: CONTENT_ID_KEY, + routerParams: { + initialEntries: [{ pathname: '/content/ansible_collections/51', hash: '#/details' }], + initialIndex: 1, + }, + }; + + const { name, version } = ansibleCollectionDetailsResponse; + const ansibleCollectionsScope = nockInstance + .get(ansibleCollectionDetailsPath) + .query(true) + .reply(200, ansibleCollectionDetailsResponse); + + const { queryByText, getAllByText, getByLabelText } = + renderWithRedux(withContentRoute(), renderOptions); + + expect(queryByText(name)).toBeNull(); + await patientlyWaitFor(() => { + expect(getAllByText(name)[0]).toBeInTheDocument(); + expect(getAllByText(version)[0]).toBeInTheDocument(); + expect(getByLabelText('content_breadcrumb')).toBeInTheDocument(); + expect(getByLabelText('content_breadcrumb_content')).toHaveTextContent(name); + }); + + assertNockRequest(ansibleCollectionsScope, done); +}); + +test('Can call API for Ansible collection repository details and show repositories tab', async (done) => { + const autocompleteUrl = '/repositories/auto_complete_search'; + const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); + searchDelayScope = mockSetting(nockInstance, 'autosearch_delay', 500); + autoSearchScope = mockSetting(nockInstance, 'autosearch_while_typing', true); + + const results = ansibleCollectionRepositoryDetailsResponse.results[0]; + const repoName = results.name; + const productName = results.product.name; + const lastSyncWords = `${results.last_sync_words} ago`; + const contentCountWords = '17 Ansible collections'; + + const ansibleCollectionRepositoryDetailsScope = nockInstance + .get(contentRepositoryDetailsPath) + .query(true) + .reply(200, ansibleCollectionRepositoryDetailsResponse); + + const { queryByText, getAllByText } = + renderWithRedux(); + + expect(queryByText(repoName)).toBeNull(); + await patientlyWaitFor(() => { + expect(getAllByText(repoName)[0]).toBeInTheDocument(); + expect(getAllByText(productName)[0]).toBeInTheDocument(); + expect(getAllByText(lastSyncWords)[0]).toBeInTheDocument(); + expect(getAllByText(contentCountWords)[0]).toBeInTheDocument(); + }); + + assertNockRequest(autoSearchScope); + assertNockRequest(searchDelayScope); + assertNockRequest(autocompleteScope); + assertNockRequest(ansibleCollectionRepositoryDetailsScope, done); +}); diff --git a/webpack/scenes/Content/Table/ContentTable.js b/webpack/scenes/Content/Table/ContentTable.js index 02f23464934..203cf9ad41b 100644 --- a/webpack/scenes/Content/Table/ContentTable.js +++ b/webpack/scenes/Content/Table/ContentTable.js @@ -9,7 +9,9 @@ import { selectContent, selectContentStatus, selectContentError } from '../Conte import SelectableDropdown from '../../../components/SelectableDropdown'; import contentConfig from '../ContentConfig'; /* eslint-disable react/no-array-index-key */ -const ContentTable = ({ selectedContentType, setSelectedContentType, contentTypes }) => { +const ContentTable = ({ + selectedContentType, setSelectedContentType, contentTypes, showContentTypeSelector, +}) => { const status = useSelector(selectContentStatus); const error = useSelector(selectContentError); const response = useSelector(selectContent); @@ -41,6 +43,7 @@ const ContentTable = ({ selectedContentType, setSelectedContentType, contentType [contentTypes, selectedContentType], )} actionButtons={ + showContentTypeSelector && { + const mockContentTypes = { 'Ansible Collections': ['ansible_collection', 'ansible_collections'] }; + const autocompleteUrl = '/ansible_collections/auto_complete_search'; + const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); + + const { results } = ansibleCollectionsResponse; + const [firstPackage] = results; + + const ansibleCollections = nockInstance + .get(ansibleCollectionsPath) + .query(true) + .reply(200, ansibleCollectionsResponse); + + const { queryByText, getAllByText } = + renderWithRedux( {}} + showContentTypeSelector={false} + />); + + expect(queryByText(firstPackage.name)).toBeNull(); + await patientlyWaitFor(() => { + expect(getAllByText(firstPackage.name)[0]).toBeInTheDocument(); + expect(getAllByText(firstPackage.version)[0]).toBeInTheDocument(); + expect(getAllByText(firstPackage.checksum)[0]).toBeInTheDocument(); + }); + assertNockRequest(autocompleteScope); + assertNockRequest(ansibleCollections, done); +}); diff --git a/webpack/scenes/Content/__tests__/contentTypes.fixtures.json b/webpack/scenes/Content/__tests__/contentTypes.fixtures.json index 11e2c98b0c8..3a68f1e2c44 100644 --- a/webpack/scenes/Content/__tests__/contentTypes.fixtures.json +++ b/webpack/scenes/Content/__tests__/contentTypes.fixtures.json @@ -1,7 +1,7 @@ [ { "label": "ansible collection", - "generic_browser": null + "generic_browser": true }, { "label": "deb",