コード例 #1
0
    def test_redhat_sync_1(self):
        """@Test: Sync RedHat Repository.

        @Feature: Repositories

        @Assert: Repository synced should fetch the data successfully.

        """
        cloned_manifest_path = manifests.clone()
        org_id = entities.Organization().create()['id']
        repo = "Red Hat Enterprise Linux 6 Server - RH Common RPMs x86_64 6.3"
        task = entities.Organization(id=org_id).upload_manifest(
            path=cloned_manifest_path)
        self.assertEqual(u'success', task['result'],
                         task['humanized']['errors'])
        repo_id = utils.enable_rhrepo_and_fetchid(
            "x86_64",
            org_id,
            "Red Hat Enterprise Linux Server",
            repo,
            "Red Hat Enterprise Linux 6 Server - RH Common (RPMs)",
            "6.3",
        )
        task_result = entities.Repository(id=repo_id).sync()['result']
        self.assertEqual(task_result, u'success',
                         u"Sync for repository '{0}' failed.".format(repo))
コード例 #2
0
    def test_update_resource(self, testdata):
        """@Test: Update a libvirt Compute Resource's Organization

        @Feature: Compute Resource - Update

        @Assert: The libvirt Compute Resource is updated

        """
        name = testdata['name']
        newname = testdata['newname']
        org_name1 = entities.Organization(
            name=gen_string("alpha", 8)).create_json()['name']
        org_name2 = entities.Organization(
            name=gen_string("alpha", 8)).create_json()['name']
        libvirt_url = "qemu+tcp://%s:16509/system"
        provider_type = FOREMAN_PROVIDERS['libvirt']
        url = (libvirt_url % conf.properties['main.server.hostname'])
        with Session(self.browser) as session:
            make_resource(session,
                          name=name,
                          orgs=[org_name1],
                          provider_type=provider_type,
                          url=url,
                          org_select=True)
            search = self.compute_resource.search(name)
            self.assertIsNotNone(search)
            self.compute_resource.update(name,
                                         newname, [org_name1], [org_name2],
                                         libvirt_set_passwd=False)
            search = self.compute_resource.search(newname)
            self.assertIsNotNone(search)
コード例 #3
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_negative_create_duplicate(self):
        """@Test: Create two organizations with identical names.

        @Assert: The second organization cannot be created.

        @Feature: Organization

        """
        name = entities.Organization.name.gen_value()
        entities.Organization(name=name).create_json()
        with self.assertRaises(HTTPError):
            entities.Organization(name=name).create_json()
コード例 #4
0
ファイル: test_subscription.py プロジェクト: cswiii/robottelo
    def test_positive_create_1(self):
        """@Test: Upload a manifest.

        @Assert: Manifest is uploaded successfully

        @Feature: Subscriptions

        """
        cloned_manifest_path = manifests.clone()
        org_id = entities.Organization().create()['id']
        task = entities.Organization(id=org_id).upload_manifest(
            path=cloned_manifest_path)
        self.assertEqual(u'success', task['result'],
                         task['humanized']['errors'])
コード例 #5
0
ファイル: test_entities.py プロジェクト: cswiii/robottelo
    def test_delete_manifest(self, http_status_code):
        """Call :meth:`robottelo.entities.Organization.delete_manifest`.

        Assert that ``Organization.delete_manifest`` returns a dictionary
        when an HTTP 202 or some other success status code is returned.

        """
        # `client.post` will return this.
        post_return = mock.Mock()
        post_return.status_code = http_status_code
        post_return.raise_for_status.return_value = None
        post_return.json.return_value = {'id': gen_integer()}  # mock task ID

        # Start by patching `client.post` and `ForemanTask.poll`...
        # NOTE: Python 3 allows for better nested context managers.
        with mock.patch.object(client, 'post') as client_post:
            client_post.return_value = post_return
            with mock.patch.object(entities.ForemanTask, 'poll') as ft_poll:
                ft_poll.return_value = {}

                # ... then see if `delete_manifest` acts correctly.
                for synchronous in (True, False):
                    reply = entities.Organization(
                        id=self.entity_id
                    ).delete_manifest(synchronous)
                    self.assertIsInstance(reply, dict)
コード例 #6
0
ファイル: test_contentviews.py プロジェクト: cswiii/robottelo
    def test_associate_view_rh_1(self):
        """@test: associate Red Hat content in a view

        @feature: Content Views

        @setup: Sync RH content

        @assert: RH Content can be seen in a view

        """
        cv_name = gen_string("alpha", 8)
        rh_repo = {
            'name': ("Red Hat Enterprise Linux 6 Server "
                     "- RH Common RPMs x86_64 6.3"),
            'product':
            "Red Hat Enterprise Linux Server",
            'reposet': ("Red Hat Enterprise Linux 6 Server "
                        "- RH Common (RPMs)"),
            'basearch':
            "x86_64",
            'releasever':
            "6.3"
        }
        # Create new org to import manifest
        org_attrs = entities.Organization().create()
        org_id = org_attrs['id']
        with Session(self.browser) as session:
            self.setup_to_create_cv(rh_repo=rh_repo, org_id=org_id)
            # Create content-view
            make_contentview(session, org=org_attrs['name'], name=cv_name)
            self.assertIsNotNone(self.content_views.search(cv_name))
            self.content_views.add_remove_repos(cv_name, [rh_repo['name']])
            self.assertIsNotNone(
                self.content_views.wait_until_element(
                    common_locators["alert.success"]))
コード例 #7
0
    def test_positive_update_3(self, plan_name):
        """@Test: Update Sync plan and associate products

        @Feature: Content Sync Plan - Positive Update add products

        @Assert: Sync Plan has the associated product

        """
        strategy, value = locators["sp.prd_select"]
        product_name = entities.Product(
            organization=self.org_id
        ).create()['name']
        entities.Organization(id=self.org_id).sync_plan(
            name=plan_name,
            interval=SYNC_INTERVAL['week']
        )
        with Session(self.browser) as session:
            session.nav.go_to_select_org(self.org_name)
            session.nav.go_to_sync_plans()
            self.syncplan.update(plan_name, add_products=[product_name])
            self.syncplan.search(plan_name).click()
            self.syncplan.wait_for_ajax()
            # Assert product is associated with sync plan
            self.syncplan.wait_until_element(
                tab_locators["sp.tab_products"]).click()
            self.syncplan.wait_for_ajax()
            element = self.syncplan.wait_until_element(
                (strategy, value % product_name))
            self.assertIsNotNone(element)
コード例 #8
0
    def test_positive_create_2(self, prd_name):
        """@Test: Create Content Product with same name but in another org

        @Feature: Content Product - Positive Create

        @Assert: Product is created successfully in both the orgs.

        """
        description = "test 123"
        org2_name = entities.Organization(
            name=gen_string("alpha", 8)).create()['name']
        with Session(self.browser) as session:
            make_product(session,
                         org=self.org_name,
                         loc=self.loc_name,
                         name=prd_name,
                         description=description)
            self.assertIsNotNone(self.products.search(prd_name))
            make_product(session,
                         org=org2_name,
                         loc=self.loc_name,
                         name=prd_name,
                         description=description,
                         force_context=True)
            self.assertIsNotNone(self.products.search(prd_name))
コード例 #9
0
    def test_positive_create_user_29(self):
        """@Test: Create User and associate a default Org

        @Feature: User - Positive Create.

        @Assert: User is created with default Org selected.

        """
        strategy, value = common_locators["entity_deselect"]
        name = gen_string("alpha", 6)
        org_name = gen_string("alpha", 6)
        entities.Organization(name=org_name).create()
        with Session(self.browser) as session:
            make_user(session,
                      username=name,
                      organizations=[org_name],
                      edit=True,
                      default_org=org_name)
            self.user.search(name, search_key).click()
            session.nav.wait_until_element(
                tab_locators["users.tab_organizations"]).click()
            element = session.nav.wait_until_element(
                (strategy, value % org_name))
            self.assertIsNotNone(element)
            org_element = session.nav.find_element(
                locators["users.default_org"])
            # Fetches currently selected option in a normal select.
            option = Select(org_element).first_selected_option
            self.assertEqual(org_name, option.text)
コード例 #10
0
    def test_positive_create_user_25(self):
        """@Test: Create User associated to multiple Orgs

        @Feature: User - Positive Create

        @Assert: User is created

        """
        strategy, value = common_locators["entity_deselect"]
        name = gen_string("alpha", 6)
        org_name1 = gen_string("alpha", 6)
        org_name2 = gen_string("alpha", 6)
        for org_name in [org_name1, org_name2]:
            entities.Organization(name=org_name).create()
        with Session(self.browser) as session:
            make_user(session,
                      username=name,
                      organizations=[org_name1, org_name2],
                      edit=True)
            self.user.search(name, search_key).click()
            self.user.wait_until_element(
                tab_locators["users.tab_organizations"]).click()
            for org_name in [org_name1, org_name2]:
                element = self.user.wait_until_element(
                    (strategy, value % org_name))
                self.assertIsNotNone(element)
コード例 #11
0
ファイル: test_product.py プロジェクト: cswiii/robottelo
    def test_positive_create_2(self):
        """@Test: Create a product and provide a GPG key.

        @Assert: A product is created with the specified GPG key.

        @Feature: Product

        """
        # Create an organization, GPG key and product.
        #
        # * GPG key points to organization
        # * Product points to organization and GPG key
        #
        # Re-using an organization speeds up the test.
        org_attrs = entities.Organization().create()
        gpgkey_attrs = entities.GPGKey(
            content=read_data_file(VALID_GPG_KEY_FILE),
            organization=org_attrs['id']
        ).create()
        product_attrs = entities.Product(
            gpg_key=gpgkey_attrs['id'],
            organization=org_attrs['id']
        ).create()

        # GET the product and verify it's GPG key ID.
        attrs = entities.Product(id=product_attrs['id']).read_json()
        self.assertEqual(attrs['gpg_key_id'], gpgkey_attrs['id'])
コード例 #12
0
    def test_subscribe_system_to_cv(self):
        """@Test: Subscribe a system to a content view.

        @Feature: ContentView

        @Assert: It is possible to create a system and set its
        'content_view_id' attribute.

        """
        # organization
        # ├── lifecycle environment
        # └── content view
        org = entities.Organization()
        org.id = org.create()['id']
        lifecycle_env = entities.LifecycleEnvironment(organization=org.id)
        lifecycle_env.id = lifecycle_env.create()['id']
        content_view = entities.ContentView(organization=org.id)
        content_view.id = content_view.create()['id']

        # Publish the content view.
        response = content_view.publish()
        humanized_errors = response['humanized']['errors']
        _check_bz_1186432(humanized_errors)
        self.assertEqual(response['result'], 'success', humanized_errors)

        # Get the content view version's ID.
        response = client.get(
            entities.ContentViewVersion().path(),
            auth=get_server_credentials(),
            data={u'content_view_id': content_view.id},
            verify=False,
        )
        response.raise_for_status()
        results = response.json()['results']
        self.assertEqual(len(results), 1)
        cv_version = entities.ContentViewVersion(id=results[0]['id'])

        # Promote the content view version.
        response = cv_version.promote(environment_id=lifecycle_env.id)
        humanized_errors = response['humanized']['errors']
        _check_bz_1186432(humanized_errors)
        self.assertEqual('success', response['result'], humanized_errors)

        # Create a system that is subscribed to the published and promoted
        # content view. Associating this system with the organization and
        # environment created above is not particularly important, but doing so
        # means a shorter test where fewer entities are created, as
        # System.organization and System.environment are required attributes.
        system_attrs = entities.System(
            content_view=content_view.id,
            environment=lifecycle_env.id,
            organization=org.id,
        ).create()

        # See BZ #1151240
        self.assertEqual(system_attrs['content_view_id'], content_view.id)
        self.assertEqual(system_attrs['environment']['id'], lifecycle_env.id)
        self.assertEqual(system_attrs['organization_id'], org.id)
コード例 #13
0
ファイル: test_syncplan.py プロジェクト: cswiii/robottelo
 def setUpClass(cls):
     """Create an organization and products which can be re-used in
     tests."""
     cls.org_id = entities.Organization().create_json()['id']
     cls.prod1_id = entities.Product(
         organization=cls.org_id).create_json()['id']
     cls.prod2_id = entities.Product(
         organization=cls.org_id).create_json()['id']
     super(SyncPlanSynchronizeTestCase, cls).setUpClass()
コード例 #14
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_positive_search(self):
        """@Test: Create an organization, then search for it by name.

        @Assert: Searching returns at least one result.

        @Feature: Organization

        """
        name = entities.Organization().create_json()['name']
        response = client.get(
            entities.Organization().path(),
            auth=get_server_credentials(),
            data={u'name': name},
            verify=False,
        )
        response.raise_for_status()
        results = response.json()['results']
        self.assertGreaterEqual(len(results), 1)
コード例 #15
0
    def setUpClass(cls):  # noqa
        org_attrs = entities.Organization().create()
        loc_attrs = entities.Location().create()
        cls.org_name = org_attrs['name']
        cls.org_id = org_attrs['id']
        cls.loc_name = loc_attrs['name']
        cls.loc_id = loc_attrs['id']

        super(Repos, cls).setUpClass()
コード例 #16
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_negative_create_name(self, name):
        """@Test: Create an org with an incorrect name.

        @Assert: The organization cannot be created.

        @Feature: Organization

        """
        with self.assertRaises(HTTPError):
            entities.Organization(name=name).create_json()
コード例 #17
0
ファイル: test_subscription.py プロジェクト: cswiii/robottelo
    def test_negative_create_1(self):
        """@Test: Upload the same manifest to two organizations.

        @Assert: The manifest is not uploaded to the second organization.

        @Feature: Subscriptions

        """
        manifest_path = manifests.clone()

        # Upload the manifest to one organization.
        org_id = entities.Organization().create_json()['id']
        task = entities.Organization(id=org_id).upload_manifest(manifest_path)
        self.assertEqual('success', task['result'],
                         task['humanized']['errors'])

        # Upload the manifest to a second organization.
        org = entities.Organization()
        org.id = org.create_json()['id']
        self.assertNotEqual('success',
                            org.upload_manifest(manifest_path)['result'])
        self.assertEqual([], org.subscriptions())
コード例 #18
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_positive_create_3(self):
        """@Test: Create an organization and provide a name and label.

        @Assert: The organization has the provided attributes.

        @Feature: Organization

        """
        name = entities.Organization.name.gen_value()
        label = entities.Organization.label.gen_value()
        attrs = entities.Organization(name=name, label=label).create_json()
        self.assertEqual(attrs['name'], name)
        self.assertEqual(attrs['label'], label)
コード例 #19
0
ファイル: test_contentviews.py プロジェクト: cswiii/robottelo
    def test_cv_promote_rh_1(self):
        """@test: attempt to promote a content view containing RH content

        @feature: Content Views

        @setup: Multiple environments for an org; RH content synced

        @assert: Content view can be promoted

        """
        cv_name = gen_string("alpha", 8)
        rh_repo = {
            'name': ("Red Hat Enterprise Linux 6 Server "
                     "- RH Common RPMs x86_64 6.3"),
            'product':
            "Red Hat Enterprise Linux Server",
            'reposet': ("Red Hat Enterprise Linux 6 Server "
                        "- RH Common (RPMs)"),
            'basearch':
            "x86_64",
            'releasever':
            "6.3"
        }
        env_name = gen_string("alpha", 8)
        publish_version = "Version 1"
        strategy, value = locators["content_env.select_name"]
        # Create new org to import manifest
        org_attrs = entities.Organization().create()
        org_id = org_attrs['id']
        org_name = org_attrs['name']
        with Session(self.browser) as session:
            make_lifecycle_environment(session, org=org_name, name=env_name)
            self.assertIsNotNone(
                session.nav.wait_until_element((strategy, value % env_name)))
            self.setup_to_create_cv(rh_repo=rh_repo, org_id=org_id)
            # Create content-view
            make_contentview(session, org=org_name, name=cv_name)
            self.assertIsNotNone(self.content_views.search(cv_name))
            self.content_views.add_remove_repos(cv_name, [rh_repo['name']])
            self.assertIsNotNone(
                self.content_views.wait_until_element(
                    common_locators["alert.success"]))
            self.content_views.publish(cv_name)
            self.assertIsNotNone(
                self.content_views.wait_until_element(
                    common_locators["alert.success"]))
            self.content_views.promote(cv_name, publish_version, env_name)
            self.assertIsNotNone(
                self.content_views.wait_until_element(
                    common_locators["alert.success"]))
コード例 #20
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_positive_create_1(self):
        """@Test: Create an organization and provide a name.

        @Assert: The organization has the provided attributes and an
        auto-generated label.

        @Feature: Organization

        """
        attrs = entities.Organization().create_json()
        self.assertTrue('label' in attrs.keys())
        if sys.version_info[0] is 2:
            self.assertIsInstance(attrs['label'], unicode)
        else:
            self.assertIsInstance(attrs['label'], str)
コード例 #21
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_positive_create_2(self):
        """@Test: Create an org and provide a name and identical label.

        @Assert: The organization has the provided attributes.

        @Feature: Organzation

        """
        # A label has a more restrictive allowable charset than a name.
        name_label = entities.Organization.label.gen_value()
        attrs = entities.Organization(
            name=name_label,
            label=name_label,
        ).create_json()
        self.assertEqual(attrs['name'], name_label)
        self.assertEqual(attrs['label'], name_label)
コード例 #22
0
    def test_positive_delete_1(self, plan_name):
        """@Test: Delete a Sync plan

        @Feature: Content Sync Plan - Positive Delete

        @Assert: Sync Plan is deleted

        """
        entities.Organization(id=self.org_id).sync_plan(
            name=plan_name,
            interval=SYNC_INTERVAL['day']
        )
        with Session(self.browser) as session:
            session.nav.go_to_select_org(self.org_name)
            session.nav.go_to_sync_plans()
            self.syncplan.delete(plan_name)
            self.assertIsNone(self.syncplan.search(plan_name))
コード例 #23
0
ファイル: test_entities.py プロジェクト: cswiii/robottelo
    def test_subscriptions(self):
        """Call :meth:`robottelo.entities.Organization.subscriptions`.

        Asserts that ``entities.Organization.subscriptions`` returns a list.

        """
        # Create a mock server response object.
        mock_response = mock.Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status.return_value = None
        mock_response.json.return_value = {u'results': []}

        with mock.patch.object(client, 'get') as mocked_client_get:
            mocked_client_get.return_value = mock_response
            # See if `subscriptions` behaves correctly.
            response = entities.Organization(id=self.entity_id).subscriptions()
            self.assertEqual(response, [])
コード例 #24
0
ファイル: test_contentviews.py プロジェクト: cswiii/robottelo
    def setup_to_create_cv(self,
                           repo_name=None,
                           repo_url=None,
                           repo_type=None,
                           rh_repo=None,
                           org_id=None):
        """Create product/repo and sync it"""

        if not rh_repo:
            repo_name = repo_name or gen_string("alpha", 8)

            # Creates new custom product via API's
            product_attrs = entities.Product(
                organization=org_id or self.org_id).create()

            # Creates new custom repository via API's
            repo_attrs = entities.Repository(
                name=repo_name,
                url=(repo_url or FAKE_1_YUM_REPO),
                content_type=(repo_type or REPO_TYPE['yum']),
                product=product_attrs['id'],
            ).create()
            repo_id = repo_attrs['id']
        elif rh_repo:
            # Clone the manifest and fetch it's path.
            manifest_path = manifests.clone()
            # Uploads the manifest and returns the result.
            task = entities.Organization(id=org_id).upload_manifest(
                path=manifest_path)
            self.assertEqual(u'success', task['result'],
                             task['humanized']['errors'])
            # Enables the RedHat repo and fetches it's Id.
            repo_id = utils.enable_rhrepo_and_fetchid(
                rh_repo['basearch'],
                str(org_id),  # Org Id is passed as data in API hence str
                rh_repo['product'],
                rh_repo['name'],
                rh_repo['reposet'],
                rh_repo['releasever'])
            repo_name = rh_repo['name']

        # Sync repository
        task_result = entities.Repository(id=repo_id).sync()['result']
        self.assertEqual(task_result, u'success',
                         u"Sync for repository {0} failed.".format(repo_name))
コード例 #25
0
    def test_get_all(self):
        """@Test: Get ``katello/api/v2/environments`` and specify just an
        organization ID.

        @Feature: LifecycleEnvironment

        @Assert: HTTP 200 is returned with an ``application/json`` content-type

        """
        org_attrs = entities.Organization().create()
        response = client.get(
            entities.LifecycleEnvironment().path(),
            auth=get_server_credentials(),
            data={u'organization_id': org_attrs['id']},
            verify=False,
        )
        self.assertEqual(response.status_code, httplib.OK)
        self.assertIn('application/json', response.headers['content-type'])
コード例 #26
0
    def test_positive_update_1(self, plan_name):
        """@Test: Update Sync plan's name

        @Feature: Content Sync Plan - Positive Update name

        @Assert: Sync Plan's name is updated

        """
        new_plan_name = gen_string("alpha", 8)
        entities.Organization(id=self.org_id).sync_plan(
            name=plan_name,
            interval=SYNC_INTERVAL['day']
        )
        with Session(self.browser) as session:
            session.nav.go_to_select_org(self.org_name)
            session.nav.go_to_sync_plans()
            self.syncplan.update(plan_name, new_name=new_plan_name)
            self.assertIsNotNone(self.syncplan.search(new_plan_name))
コード例 #27
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_create_text_plain(self):
        """@Test Create an organization using a 'text/plain' content-type.

        @Assert: HTTP 415 is returned.

        @Feature: Organization

        """
        organization = entities.Organization()
        organization.create_missing()
        response = client.post(
            organization.path(),
            organization.create_payload(),
            auth=get_server_credentials(),
            headers={'content-type': 'text/plain'},
            verify=False,
        )
        self.assertEqual(httplib.UNSUPPORTED_MEDIA_TYPE, response.status_code)
コード例 #28
0
ファイル: test_organization.py プロジェクト: cswiii/robottelo
    def test_positive_create_5(self):
        """@Test: Create an org and provide a name, label and description.

        @Assert: The organization has the provided name, label and description.

        @Feature: Organization

        """
        name = entities.Organization.name.gen_value()
        label = entities.Organization.label.gen_value()
        description = entities.Organization.description.gen_value()
        attrs = entities.Organization(
            name=name,
            label=label,
            description=description,
        ).create_json()
        self.assertEqual(attrs['name'], name)
        self.assertEqual(attrs['label'], label)
        self.assertEqual(attrs['description'], description)
コード例 #29
0
ファイル: test_location.py プロジェクト: cswiii/robottelo
    def test_positive_create_6(self, test_data):
        """@test: Select both organization and location.

        @feature: Locations

        @assert: Both organization and location are selected.

        """
        org_name = test_data['org_name']
        loc_name = test_data['loc_name']
        org = entities.Organization(name=org_name).create()
        self.assertEqual(org['name'], org_name)
        with Session(self.browser) as session:
            make_loc(session, name=loc_name)
            self.assertIsNotNone(self.location.search(loc_name))
            location = session.nav.go_to_select_loc(loc_name)
            organization = session.nav.go_to_select_org(org_name)
            self.assertEqual(location, loc_name)
            self.assertEqual(organization, org_name)
コード例 #30
0
    def test_positive_update_4(self, plan_name):
        """@Test: Update Sync plan and disassociate products

        @Feature: Content Sync Plan - Positive Update remove products

        @Assert: Sync Plan does not have the associated product

        """
        strategy, value = locators["sp.prd_select"]
        product_name = entities.Product(
            organization=self.org_id
        ).create()['name']
        entities.Organization(id=self.org_id).sync_plan(
            name=plan_name,
            interval=SYNC_INTERVAL['week']
        )
        with Session(self.browser) as session:
            session.nav.go_to_select_org(self.org_name)
            session.nav.go_to_sync_plans()
            self.syncplan.update(plan_name, add_products=[product_name])
            self.syncplan.search(plan_name).click()
            self.syncplan.wait_for_ajax()
            self.syncplan.wait_until_element(
                tab_locators["sp.tab_products"]).click()
            self.syncplan.wait_for_ajax()
            element = self.syncplan.wait_until_element(
                (strategy, value % product_name))
            self.assertIsNotNone(element)
            # Dis-associate the product from sync plan and the selected product
            # should automatically move from 'List/Remove` tab to 'Add' tab
            self.syncplan.update(plan_name, rm_products=[product_name])
            self.syncplan.search(plan_name).click()
            self.syncplan.wait_for_ajax()
            self.syncplan.wait_until_element(
                tab_locators["sp.tab_products"]).click()
            self.syncplan.wait_for_ajax()
            self.syncplan.wait_until_element(
                tab_locators["sp.add_prd"]).click()
            self.syncplan.wait_for_ajax()
            element = self.syncplan.wait_until_element(
                (strategy, value % product_name))
            self.assertIsNotNone(element)