示例#1
0
 def setup_class(self):
     super().setup_class(self)
     self.db = HiveDatabase(os.environ['DATABASE_URL'])
     self.shop_name = os.environ['SHOP_NAME']
     self.shop_id = self.db.get_shop_id(self.shop_name)
     self.user_id = self.db.get_user_id(VELA_USER)
     self.user_db_name = self.db.get_user_db_name(VELA_USER)
     self.company_id = self.db.get_company_id(VELA_USER)
示例#2
0
 def setup_class(self):
     super().setup_class(self)
     self.db = HiveDatabase(os.environ['DATABASE_URL'])
     self.shop_name = os.environ['SHOP_NAME']
     self.shop_id = self.db.get_shop_id(self.shop_name)
     self.shop_domain = self.db.get_shop_domain(self.shop_id)
     (self.shop_token, _) = self.db.get_shop_tokens(self.shop_id)
     self.user_id = self.db.get_user_id(VELA_USER)
示例#3
0
    def setup_method(self, method):
        super().setup_method(method)
        setup_rabbit()
        self.set_etsy_testcase('listings_16')
        self.stop_all()
        run_sql('HIVE', 'listings_16_changed', retry=2)
        run_sql('HIVE', 'update_shops_timestamp', retry=2)
        self.restart_all()

        self.db = HiveDatabase(HIVE_DATABASE_URL)

        lp = LoginPage(self.driver)
        lp.login(page=self.login_url_http)

        # Select both listings and go to bulk editor
        mp = MainPage(self.driver)
        mp.get_main(self.base_url)
        mp.select_filter_tab('Active')

        mp.select_listings_to_edit(checked_listings='ALL')
示例#4
0
    def test_etsy_duplicate_sections(self):
        """ Test verifies the situation when section is defined in Etsy then user creates the
        same section in VELA and synces - section must not be duplicated in our DB, its etsy ID should be updated only.
        For more info see HIVE-996.
        """

        expected_db_data_queue = [
            ['1', 'syncShop', '2', 'done', '']
        ]

        expected_db_data_sections = [
            ['1', '2', '15183328', 'On Sale'],
            ['2', '2', '15180189', 'Holiday Gifts'],
            ['3', '2', '17365192', 'Summer Sale'],
            ['4', '2', '18790753', 'de'],
            ['5', '2', '18787742', 'bbbaa'],
            ['6', '2', '18790755', 'eeee']
        ]

        self.stop_all()
        self.set_etsy_testcase('listings_10')
        run_sql('HIVE', 'listings_10', retry=2)
        db = HiveDatabase(HIVE_DATABASE_URL)
        # set section etsy ID to NULL in test data
        # TODO: This should be reworked, section should be deleted from test data and created in UI as new
        db.change_section_etsy_id(None, 'eeee')
        self.restart_all()

        # check that sync task is done
        wait_for_assert(
            expected_db_data_queue,
            lambda: run_sql('HIVE', 'select_operations', True),
            'syncShop task was not completed as expected')

        # check that sections are synced correctly, there are no duplicates
        data = run_sql('HIVE', 'select_sections', True)
        assert data == expected_db_data_sections
示例#5
0
class TestEtsyUpload(BaseTestClass):

    NEW_LISTING = {
        'quantity': 10,
        'title': 'auto_test_listing_01',
        'description': 'description',
        'price': 10,
        'who_made': 'i_did',
        'is_supply': True,
        'when_made': 'made_to_order',
        'state': 'draft'
    }

    def setup_class(self):
        super().setup_class(self)
        self.db = HiveDatabase(os.environ['DATABASE_URL'])
        self.shop_name = os.environ['SHOP_NAME']
        self.shop_id = self.db.get_shop_id(self.shop_name)
        self.user_id = self.db.get_user_id(VELA_USER)
        self.user_db_name = self.db.get_user_db_name(VELA_USER)
        self.company_id = self.db.get_company_id(VELA_USER)

    def get_credentials(self):
        (token, secret) = self.db.get_shop_tokens(self.shop_id)
        return {
            'CLIENT_KEY': os.environ['VELA_CLIENT_TOKEN'],
            'CLIENT_SECRET': os.environ['VELA_CLIENT_SECRET'],
            'AUTHORIZED_OAUTH_TOKEN': token,
            'AUTHORIZED_OAUTH_TOKEN_SECRET': secret,
        }

    def reload_shop(self):
        self.db.set_shop_last_sync_time(self.shop_id, FAKE_SYNC_TIME_STR)
        vela.trigger_etsy_shop_sync(self.company_id,
                                    self.shop_id,
                                    db_name=self.user_db_name)

        print("Waiting for shop to download ")
        for i in range(SHOP_SYNC_TIMEOUT):
            sleep(1)
            if self.db.get_shop_last_sync_time(self.shop_id) != FAKE_SYNC_TIME:
                break
        assert self.db.get_shop_last_sync_time(
            self.shop_id) != FAKE_SYNC_TIME, "Shop " + str(
                self.shop_id) + " is not 'up_to_date'"

    def wait_for_synced(self):
        log("Waiting for changes to be uploaded to Etsy")
        wait_for_assert('up_to_date',
                        lambda: self.db.get_shop_status(self.shop_id),
                        'Shop not synced',
                        retries=SHOP_SYNC_TIMEOUT)
        log("Shop synced")

    def go_to_bulk(self):
        # log in
        lpg = LoginPage(self.driver)
        lpg.login(page=self.login_url_https,
                  user=VELA_USER,
                  password=VELA_PASSWORD)

        # switch the shop if needed
        mp = MainPage(self.driver)
        if mp.shop_name_text() != self.shop_name:
            mp.change_shop(self.shop_name)

        # select listings to edit
        mp.select_listings_to_edit(checked_listings=[
            "auto_test_listing_00", "auto_test_listing_01",
            "auto_test_listing_02"
        ],
                                   status='Draft',
                                   filter='auto_')

    def validate_listings(self, listings, timestamp):
        for i, listing in enumerate(listings[key]
                                    for key in sorted(listings.keys())):
            id = "{}_{:02d}".format(AT_TITLE, i)
            log("Verifying ", id)

            assert listing['title'] == id + " " + timestamp
            assert listing['description'] == AT_DESCRIPTION + " " + timestamp
            assert listing['taxonomy_id'] == AT_TAXONOMY_ID
            assert listing['taxonomy_path'] == AT_TAXONOMY_PATH_API
            image_urls = [
                image['url_fullxfull'] for image in listing['Images']
            ]
            compare_images(image_urls, AT_IMAGES)
            assert listing['tags'] == AT_TAGS
            assert listing['materials'] == AT_MATERIALS
            assert listing['Section']['title'] == AT_SECTION

            attributes = set()
            for attribute in listing['Attributes']:
                assert len(attribute['values']) == 1
                attributes.add(
                    (attribute['property_name'], attribute['values'][0]))
            assert attributes == AT_ATTRIBUTES_API

            products = listing['Inventory'][0]['products']
            assert len(products) == len(AT_VAR_OPTION_VALUES)
            for j, option_name in enumerate(AT_VAR_OPTION_VALUES):
                assert products[j]['property_values'][0][
                    'property_name'] == AT_VAR_PROPERTY_NAME_API
                assert products[j]['property_values'][0][
                    'property_id'] == AT_VAR_PROPERTY_ID
                assert products[j]['property_values'][0]['values'][
                    0] == AT_VAR_OPTION_VALUES[j]
                assert products[j]['offerings'][0]['price'][
                    'currency_formatted_raw'] == AT_PRICE_API
                assert products[j]['offerings'][0]['quantity'] == AT_QUANTITY
                assert products[j]['sku'] == AT_SKU

    def test_etsy_upload(self):
        """
        Verify that listings can be fetched from Etsy, changed in vela GUI and pushed back to Etsy
        """

        # debug
        # with open('tests-etsy/data.json') as f:
        #     data = json.load(f)
        # self.validate_listings(data, '20161124_184056')
        # return

        # Delete log files
        logs = Logs(os.environ['LOG_CLEAN_SCRIPT'],
                    os.environ['LOG_GREP_SCRIPT'])
        logs.empty()

        timestamp = strftime("%Y%m%d_%H%M%S")

        credentials = self.get_credentials()
        etsy = EtsyApiForATs(credentials)

        # Delete our section from Etsy
        log("Deleting section from Etsy")
        etsy.remove_at_section(AT_SECTION)

        # Delete and re-create listings on Etsy
        log("Removing AT listings from Etsy")
        etsy.remove_at_listings()
        log("Creating AT listings on Etsy")
        st_id = etsy.get_shipping_template_id()
        for i in range(3):
            title = "{}_{:02d}".format(AT_TITLE, i)
            etsy.create_listing(
                dict(self.NEW_LISTING,
                     title=title,
                     taxonomy_id=1,
                     shipping_template_id=st_id))

        listings = etsy.get_listings()
        for listing_id, title in ((l['listing_id'], l['title'])
                                  for l in listings
                                  if l['title'][:len(AT_TITLE)] == AT_TITLE):
            print(listing_id, title)

        # Clean up Vela DB, Load new shop
        log("Cleaning Vela DB")
        # Reset feature flags for features that are not in production yet
        self.db.reset_user_profile_flags(self.user_id, BETA_FEATURE_FLAGS)
        self.reload_shop()

        log("Making UI changes")
        self.go_to_bulk()
        bp = BulkPage(self.driver)

        # Edit title
        log("   title")
        bp.edit_part('Title').click()
        bp.select_operation('Add After')
        send_keys(bp.operation_input(), ' ' + timestamp)
        bp.operation_apply().click()

        # Edit description
        log("   description")
        bp.edit_part('Description').click()
        bp.select_operation('Add After')
        send_keys(bp.operation_input_description(), ' ' + timestamp)
        bp.operation_apply().click()

        # Edit category
        log("   category")
        bp.edit_part('Category').click()
        bp.select_category(AT_CATEGORIES)
        bp.operation_apply().click()

        # Edit photos
        log("   photos")
        bp.edit_part('Photos').click()
        with Photos(self.driver) as photos:
            for i, img in enumerate(AT_IMAGES):
                photos.select_photo(
                    i, os.path.join(photos.photo_dir, img['file']))
            bp.operation_apply().click()

        # Edit tags
        log("   tags")
        bp.edit_part('Tags').click()
        send_keys(bp.operation_input(), ', '.join(AT_TAGS))
        bp.operation_apply().click()

        # Edit materials
        log("   materials")
        bp.edit_part('Materials').click()
        send_keys(bp.operation_input(), ', '.join(AT_MATERIALS))
        bp.operation_apply().click()

        # Edit section
        log("   section")
        bp.edit_part('Section').click()
        bp.operation_select().click()
        sleep(2)
        send_keys(bp.operation_menu_new_item_input(), AT_SECTION + Keys.RETURN)
        bp.operation_apply().click()

        # Edit Occasion
        log("   occasion")
        bp.edit_part('Occasion').click()
        bp.select_occasion(AT_OCCASION)
        bp.operation_apply().click()

        # Edit Holiday
        log("   holiday")
        bp.edit_part('Holiday').click()
        bp.select_holiday(AT_HOLIDAY)
        bp.operation_apply().click()

        # Edit variations
        log("   variations")
        bp.edit_part('Variations').click()
        biv = BulkPageInventoryVariations(self.driver, self.ts)
        bp.select_category(AT_CATEGORIES)
        bulk_row = biv.bulk_edit_row
        biv.set_property(bulk_row, 0, AT_VAR_PROPERTY_NAME)
        for option_name in AT_VAR_OPTION_VALUES:
            biv.add_option(bulk_row, 0, option_name)
        bp.operation_apply().click()

        # Edit price
        log("   price")
        bp.edit_part('Price').click()
        bip = BulkPageInventoryPrice(self.driver, self.ts)
        bip.select_operation('Change To')
        input_field = bip.operation_input()
        send_keys(input_field, AT_PRICE)
        bp.operation_apply().click()

        # Edit quantity
        log("   quantity")
        bp.edit_part('Quantity').click()
        biq = BulkPageInventoryQuantity(self.driver, self.ts)
        biq.select_operation('Change To')
        input_field = biq.operation_input()
        send_keys(input_field, str(AT_QUANTITY))
        biq.operation_apply().click()

        # Edit Sku
        log("   sku")
        bp.edit_part('SKU').click()
        bis = BulkPageInventorySku(self.driver, self.ts)
        input_field = bis.operation_input()
        send_keys(input_field, AT_SKU)
        bis.operation_apply().click()

        # Sync Updates
        log("Syncing Updates")
        bp.sync_updates_button().click()
        sleep(10)
        self.wait_for_synced()

        # Get listings from Etsy
        data = etsy.get_at_listings_details()
        self.validate_listings(data, timestamp)

        # Check logs for errors
        logs.check_for_errors()
示例#6
0
 def __init__(self, cfg: JSONConfig):
     self.cfg = cfg
     self.db = HiveDatabase(self.cfg.db.url)
     self.shop_id = self.db.get_shop_id(self.cfg.vela.shop_name)
     self.sync_status = None
     self.last_sync_timestamp = None
示例#7
0
class DatabaseControl(object):
    """
    Class that provides means to work simply with VELA DB for Health-Check
    """
    def __init__(self, cfg: JSONConfig):
        self.cfg = cfg
        self.db = HiveDatabase(self.cfg.db.url)
        self.shop_id = self.db.get_shop_id(self.cfg.vela.shop_name)
        self.sync_status = None
        self.last_sync_timestamp = None

    def _store_shop_sync_info(self):
        (self.sync_status, self.last_sync_timestamp) = \
            self.db.get_shop_info(self.shop_id, columns=[ShopInfoCols.SYNC_STATUS, ShopInfoCols.LAST_SYNC_TIMESTAMP])

    def _get_current_sync_status(self):
        self._store_shop_sync_info()
        return self.sync_status

    def wait_for_shop_up_to_date(self, timeout_sec: int):
        logging.info('Waiting for shop to be up to date')

        try:
            testing.wait_for_assert(ShopSyncStatus.UP_TO_DATE.value,
                                    lambda: self._get_current_sync_status(),
                                    retries=int(timeout_sec / 5) - 1,
                                    delay_sec=5,
                                    print_data=False)
        except AssertionError:
            raise DBControlTimeoutError('Shop is not up to date, timeout reached')
        else:
            return True
        finally:
            logging.info('Shop sync state: {} Last Sync Time: {}'.format(self.sync_status, self.last_sync_timestamp))

    def wait_for_shop_start_and_finish_syncing(self, timeout_sec: int, last_sync_timestamp: datetime):

        def _synced():
            return self.sync_status == ShopSyncStatus.UP_TO_DATE.value and last_timestamp > original_timestamp

        logging.info('Waiting for shop to be synced')
        original_timestamp = arrow.get(last_sync_timestamp)
        for i in range(int(timeout_sec / CHECK_SHOP_SYNC_STATUS_DELAY_SEC), -1, -1):
            self._store_shop_sync_info()
            last_timestamp = arrow.get(self.last_sync_timestamp)
            if _synced():
                logging.info(
                    'Shop sync state: {} Last Sync Time: {}'.format(self.sync_status, self.last_sync_timestamp))
                break
            if i:
                sleep(CHECK_SHOP_SYNC_STATUS_DELAY_SEC)
        else:
            logging.info('Shop sync state: {} Last Sync Time: {}'.format(self.sync_status, self.last_sync_timestamp))
            raise DBControlTimeoutError('Shop is not synced, timeout reached')

    def get_etsy_title(self, title_start: str):
        listings = self.db.find_etsy_products(self.shop_id, title_start + '%')
        len_listings = len(listings)
        assert len_listings == 1, 'Expected one product in DB for %s - got %d' % (title_start, len_listings)
        return listings[0][1]

    def get_shopify_title(self, title_start: str):
        products = self.db.find_shopify_products(self.shop_id, title_start + '%')
        len_products = len(products)
        assert len_products == 1, 'Expected one product in DB for %s - got %d' % (title_start, len_products)
        return products[0][1]

    def get_shop_tokens(self):
        return self.db.get_shop_tokens(self.shop_id)

    def get_shop_domain(self):
        return self.db.get_shop_domain(self.shop_id)
示例#8
0
class TestWholesaleSync(BaseTestClass):
    def setup_method(self, method):
        super().setup_method(method)
        setup_rabbit()
        self.set_etsy_testcase('listings_16')
        self.stop_all()
        run_sql('HIVE', 'listings_16_changed', retry=2)
        run_sql('HIVE', 'update_shops_timestamp', retry=2)
        self.restart_all()

        self.db = HiveDatabase(HIVE_DATABASE_URL)

        lp = LoginPage(self.driver)
        lp.login(page=self.login_url_http)

        # Select both listings and go to bulk editor
        mp = MainPage(self.driver)
        mp.get_main(self.base_url)
        mp.select_filter_tab('Active')

        mp.select_listings_to_edit(checked_listings='ALL')

    def test_wholesale_bulk_change_inventory(self):
        """ Test verifies bulk changes of inventory of listings that have different value of the flag
            'can_write_inventory' - Etsy returns false for this flag when a listing is not Retail listing. So far it is
            not possible to update inventory and attributes on such listings through API, therefore VELA doesn't allow
            to change it.
            Test also verifies that if 'can_write_inventory' is changed to false on Etsy, no inventory updates of the
            listing are sent to Etsy (HIVE-1553).
        """

        api_products_unpacked1 = [{
            'offerings': [{
                'is_enabled': 1,
                'price': 33.33,
                'quantity': 21
            }],
            'property_values': [{
                'property_id': 500,
                'property_name': 'Finish',
                'scale_id': None,
                'value': 'smooth',
                'value_id': None
            }],
            'sku':
            'NEW SKU'
        }]

        expected_api_calls = [{
            'PUT':
            '/v2/listings/100001/inventory?price_on_property=&quantity_on_property=&sku_on_property=',
            'body': {
                '_products_unpacked': api_products_unpacked1,
                'listing_id': 100001
            }
        }]

        expected_can_write_inventory = [
            ('100001', True),
            ('100002', False),
            ('100003', False),
        ]

        bp = BulkPage(self.driver)

        # ---- Make changes in Variations editor ----

        click(bp.edit_part('Variations'))

        category = ['Accessories']
        bpiv = BulkPageInventoryVariations(self.driver, self.ts)

        # Select category in bulk edit area
        bpiv.select_category(category)

        # Set first variation property and its option
        bulk_row = bpiv.bulk_edit_row
        bpiv.set_property(bulk_row, 0, 'Finish')
        bpiv.add_custom_option(bulk_row, 0, 'smooth')

        # Apply changes and check results in UI
        click(bpiv.operation_apply())
        assert 'smooth' in bpiv.listing_row('One').text
        assert bpiv.listing_row(
            'Two').text == 'Two\n' + CANNOT_EDIT_INVENTORY_TEXT
        assert 'smooth' in bpiv.listing_row('Three').text
        wait_for_web_assert(True, lambda: bpiv.is_part_modified('Variations'),
                            'Blue dot didn\'t show up for Variations editor')

        # ---- Make changes in Price, Quantity, SKU editors ----

        retail_values = {'Price': '33.33', 'Quantity': '21', 'SKU': 'NEW SKU'}

        retail_expected_values = {
            'Price': '$33.33',
            'Quantity': '21',
            'SKU': 'NEW SKU'
        }

        for editor_name in ['Price', 'Quantity', 'SKU']:
            # Switch to particular inventory editor, choose bulk operation and set the value for it
            click(bpiv.edit_part(editor_name))
            operation = 'Change To'
            bp.select_operation(operation)
            input_field = bp.operation_input()
            send_keys(input_field, retail_values[editor_name])

            # Apply changes and check results in UI
            click(bp.operation_apply())
            wait_for_web_assert(
                True, lambda: bp.is_part_modified(editor_name),
                'Blue dot didn\'t show up for %s editor' % editor_name)
            assert bp.listing_row(
                'One').text == 'One\n' + retail_expected_values[editor_name]
            assert bp.listing_row(
                'Two').text == 'Two\n' + CANNOT_EDIT_INVENTORY_TEXT
            assert bp.listing_row(
                'Three'
            ).text == 'Three\n' + retail_expected_values[editor_name]

        # Sync changes
        click(bp.sync_updates_button())

        # Check that sync button is disabled and blue dot is not displayed after clicking on Sync
        wait_for_web_assert(False,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not disabled')

        # Check API calls to Etsy emulator - only first listing should be updated
        check_etsy_emulator_requests(expected_api_calls)

        # Check can_write_inventory flags in DB - it was set to False on the listing 'Three'
        assert self.db.get_can_write_inventory(
        ) == expected_can_write_inventory

    def test_wholesale_bulk_change_occasion(self):
        """ Test verifies bulk changes of occasion of listings that have different value of the flag
            'can_write_inventory' - Etsy returns false for this flag when a listing is not Retail listing. So far it is
            not possible to update inventory and attributes on such listings through API, therefore VELA doesn't allow
            to change it.
            Test also verifies that if 'can_write_inventory' is changed to false on Etsy, no occasion
            updates of the listing are sent to Etsy (HIVE-1553).
        """

        expected_can_write_inventory = [
            ('100001', True),
            ('100002', False),
            ('100003', False),
        ]

        retail_expected_value = 'Prom'

        expected_api_calls = [{
            'PUT': '/v2/listings/100001/attributes/46803063641?value_ids=29',
            'body': {}
        }]

        # ---- Make changes in Occasion editor ----

        bp = BulkPage(self.driver)
        click(bp.edit_part('Occasion'))
        bp.select_occasion('Prom')

        # Apply changes and check listings
        click(bp.operation_apply())

        assert bp.listing_row('One').text == 'One\n' + retail_expected_value
        assert bp.listing_row(
            'Two').text == 'Two\n' + CANNOT_EDIT_OCCASION_TEXT
        assert bp.listing_row(
            'Three').text == 'Three\n' + retail_expected_value

        # Check that sync button is enabled after clicking on Apply
        wait_for_web_assert(True,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not enabled')

        # Sync changes
        click(bp.sync_updates_button())

        # Check that sync button is disabled after clicking on Sync
        wait_for_web_assert(False,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not disabled')

        # Check API calls to Etsy emulator - only first listing should be updated
        check_etsy_emulator_requests(expected_api_calls)

        # Check can_write_inventory flags in DB - it was set to False on the listing 'Three'
        assert self.db.get_can_write_inventory(
        ) == expected_can_write_inventory

    def test_wholesale_bulk_delete_occasion(self):
        """ Test verifies bulk delete of occasion of listings that have different value of the flag
            'can_write_inventory' - Etsy returns false for this flag when a listing is not Retail listing. So far it is
            not possible to update inventory and attributes on such listings through API, therefore VELA doesn't allow
            to change it.
            Test also verifies that if 'can_write_inventory' is changed to false on Etsy, no occasion
            updates of the listing are sent to Etsy (HIVE-1553).
        """

        expected_can_write_inventory = [
            ('100001', True),
            ('100002', False),
            ('100003', False),
        ]

        retail_expected_value = 'Choose Occasion'

        expected_api_calls = [{
            'DELETE': '/v2/listings/100001/attributes/46803063641',
            'body': {}
        }]

        # ---- Make changes in Occasion editor ----

        bp = BulkPage(self.driver)
        click(bp.edit_part('Occasion'))
        bp.select_occasion('None')

        # Apply changes and check listings
        click(bp.operation_apply())

        assert bp.listing_row('One').text == 'One\n' + retail_expected_value
        assert bp.listing_row(
            'Two').text == 'Two\n' + CANNOT_EDIT_OCCASION_TEXT
        assert bp.listing_row(
            'Three').text == 'Three\n' + retail_expected_value

        # Check that sync button is enabled after clicking on Apply
        wait_for_web_assert(True,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not enabled')

        # Sync changes
        click(bp.sync_updates_button())

        # Check that sync button is disabled after clicking on Sync
        wait_for_web_assert(False,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not disabled')

        # Check API calls to Etsy emulator - only first listing should be updated
        check_etsy_emulator_requests(expected_api_calls)

        # Check can_write_inventory flags in DB - it was set to False on the listing 'Three'
        assert self.db.get_can_write_inventory(
        ) == expected_can_write_inventory

    def test_wholesale_bulk_change_holiday(self):
        """ Test verifies bulk changes of holiday of listings that have different value of the flag
            'can_write_inventory' - Etsy returns false for this flag when a listing is not Retail listing. So far it is
            not possible to update inventory and attributes on such listings through API, therefore VELA doesn't allow
            to change it.
            Test also verifies that if 'can_write_inventory' is changed to false on Etsy, no holiday
            updates of the listing are sent to Etsy (HIVE-1553).
        """

        expected_can_write_inventory = [
            ('100001', True),
            ('100002', False),
            ('100003', False),
        ]

        retail_expected_value = 'Halloween'

        expected_api_calls = [{
            'PUT': '/v2/listings/100001/attributes/46803063659?value_ids=39',
            'body': {}
        }]

        # ---- Make changes in Holiday editor ----

        bp = BulkPage(self.driver)
        click(bp.edit_part('Holiday'))
        bp.select_holiday('Halloween')

        # Apply changes and check listings
        click(bp.operation_apply())

        assert bp.listing_row('One').text == 'One\n' + retail_expected_value
        assert bp.listing_row('Two').text == 'Two\n' + CANNOT_EDIT_HOLIDAY_TEXT
        assert bp.listing_row(
            'Three').text == 'Three\n' + retail_expected_value

        # Check that sync button is enabled after clicking on Apply
        wait_for_web_assert(True,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not enabled')

        # Sync changes
        click(bp.sync_updates_button())

        # Check that sync button is disabled after clicking on Sync
        wait_for_web_assert(False,
                            bp.sync_updates_button().is_enabled,
                            'Sync button is not disabled')

        # Check API calls to Etsy emulator - only first listing should be updated
        check_etsy_emulator_requests(expected_api_calls)

        # Check can_write_inventory flags in DB - it was set to False on the listing 'Three'
        assert self.db.get_can_write_inventory(
        ) == expected_can_write_inventory
示例#9
0
class TestShopifyUpload(BaseTestClass):
    def setup_class(self):
        super().setup_class(self)
        self.db = HiveDatabase(os.environ['DATABASE_URL'])
        self.shop_name = os.environ['SHOP_NAME']
        self.shop_id = self.db.get_shop_id(self.shop_name)
        self.shop_domain = self.db.get_shop_domain(self.shop_id)
        (self.shop_token, _) = self.db.get_shop_tokens(self.shop_id)
        self.user_id = self.db.get_user_id(VELA_USER)

    def go_to_bulk(self, shop_name):
        # log in
        lpg = LoginPage(self.driver)
        lpg.login(page=self.login_url_https,
                  user=VELA_USER,
                  password=VELA_PASSWORD)

        # switch the shop if needed
        mp = MainPage(self.driver)
        if mp.shop_name_text() != shop_name:
            mp.change_shop(shop_name)

        # select listings to edit
        listings_titles = [
            '{}{:02d}'.format(AT_TITLE_PREFIX, i)
            for i in range(NUMBER_OF_TEST_LISTINGS)
        ]
        mp.select_listings_to_edit(checked_listings=listings_titles,
                                   filter='auto_')

    def reload_shopify_shop(self):
        self.db.set_shop_last_sync_time(self.shop_id, FAKE_SYNC_TIME_STR)
        vela.trigger_shopify_shop_sync(self.user_id, self.shop_id)

        print("Waiting for shop to download ")
        for i in range(SHOP_SYNC_TIMEOUT):
            sleep(1)
            if self.db.get_shop_last_sync_time(self.shop_id) != FAKE_SYNC_TIME:
                break
        assert self.db.get_shop_last_sync_time(self.shop_id) != FAKE_SYNC_TIME,\
            "Shop " + str(self.shop_id) + " is not 'up_to_date'"

    def wait_for_synced(self):
        log("Waiting for changes to be uploaded to Shopify")
        wait_for_assert('up_to_date',
                        lambda: self.db.get_shop_status(self.shop_id),
                        'Shop not synced',
                        retries=SHOP_SYNC_TIMEOUT)
        log("Shop synced")

    def test_shopify_upload(self):
        """
        Verify that listings can be fetched from Shopify, changed in vela GUI and pushed back to Shopify
        """

        # --- Preparation ----

        # Delete log files
        logs = Logs(os.environ['LOG_CLEAN_SCRIPT'],
                    os.environ['LOG_GREP_SCRIPT'])
        logs.empty()

        timestamp = strftime("%Y%m%d_%H%M%S")

        # Remove test listings on shopify and create them again there
        test_listings = ShopifyTestProducts(self.shop_domain, self.shop_token,
                                            AT_TITLE_PREFIX)

        log('Removing AT listings from Shopify')
        ids = test_listings.delete_test_products()
        log('Removed listings: ' + ', '.join(map(str, ids)))

        log('Creating AT listings on Shopify')
        ids = test_listings.create_test_products(NUMBER_OF_TEST_LISTINGS)
        log('Created listings: ' + ', '.join(map(str, ids)))

        # Reload shop - sync from Shopify and disable beta features we don't want to test
        self.reload_shopify_shop()
        self.db.reset_user_profile_flags(self.user_id, BETA_FEATURE_FLAGS)

        # --- Start testing in UI ---

        log("Making UI changes")
        self.go_to_bulk(self.shop_name)
        bp = BulkPage(self.driver)

        # Edit title
        log("   title")
        bp.edit_part('Title').click()
        bp.select_operation('Add After')
        send_keys(bp.operation_input(), ' ' + timestamp)
        bp.operation_apply().click()

        # Edit description
        log("   description")
        bp.edit_part('Description').click()
        bp.select_operation('Add Before')
        send_keys(bp.operation_edit_area_description(), timestamp)
        bp.operation_apply().click()

        # Edit photos
        log("   photos")
        bp.edit_part('Photos').click()
        with Photos(self.driver) as photos:
            for i, img in enumerate(AT_IMAGES):
                photos.select_photo(
                    i, os.path.join(photos.photo_dir, img['file']))
            bp.operation_apply().click()

        # Edit tags
        log("   tags")
        bp.edit_part('Tags').click()
        send_keys(bp.operation_input(), ', '.join(AT_TAGS))
        bp.operation_apply().click()

        # Edit section
        log("   product type")
        bp.edit_part('Product Type').click()
        bp.operation_select().click()
        sleep(2)
        send_keys(bp.operation_menu_new_item_input(),
                  AT_PRODUCT_TYPE + Keys.RETURN)
        bp.operation_apply().click()

        # Edit section
        log("   vendor")
        bp.edit_part('Vendor').click()
        bp.operation_select().click()
        sleep(2)
        send_keys(bp.operation_menu_new_item_input(), AT_VENDOR + Keys.RETURN)
        bp.operation_apply().click()

        # Sync Updates
        log("Syncing Updates")
        bp.sync_updates_button().click()
        sleep(5)
        self.wait_for_synced()

        # Get test listings from Shopify and verify them
        data = test_listings.get_test_products()
        validate_listings(data, timestamp)

        # Check logs for errors
        logs.check_for_errors()
示例#10
0
 def setup_class(self):
     super().setup_class(self)
     self.db = HiveDatabase(HIVE_DATABASE_URL)
     self._used_etsy_testcase = None
示例#11
0
class TestGenerateSql(BaseTestClass):
    def setup_class(self):
        super().setup_class(self)
        self.db = HiveDatabase(HIVE_DATABASE_URL)
        self._used_etsy_testcase = None

    def import_shop(self, etsy_test_case, timeout_sec=30):
        """ Import shop from etsy emulator to Hive

        :param etsy_test_case: name of etsy test case - conforms to its json file
        :param timeout_sec: timeout for importing the shop
        """
        self._used_etsy_testcase = etsy_test_case
        self.set_etsy_testcase(etsy_test_case)
        self.stop_all()
        run_sql('HIVE', 'listings_no_shop', retry=2)
        self.restart_all()

        d = self.driver
        d.delete_all_cookies()

        lpg = LoginPage(self.driver)
        lpg.login(page=self.login_url_http)
        lpg.go_to_etsy()

        vela.wait_for_shop_to_sync(expected_status='up_to_date',
                                   timeout_sec=timeout_sec)

        self.stop_all()

        assert self.db.get_shop_status(2) == 'up_to_date'

    def save_db_dump(self, output_name):
        """ Sanitize DB contents and dump it to SQL file.

        :param output_name: name of SQL file to write the DB dump to
        """
        self.db.truncate_task_queue()
        self.db.set_shop_last_sync_time(2, OLD_SYNC_TIME_STR)
        self.db.set_product_modification_timestamp(
            OLD_PRODUCT_MODIFICATION_TIMESTAMP)

        sqldump = os.path.join(os.environ['QA_DIR'], 'bin', 'sqldump')
        process = subprocess.run(['sh', '-c', sqldump + ' --data-only'],
                                 stdout=subprocess.PIPE)
        assert process.returncode == 0

        readme = """-- README
-- Test data generated by '%s' from Etsy test case '%s'
-- END OF README -- The comments in this README section will be preserved by test data migration script.""" % \
                 (self.test_id, self._used_etsy_testcase)

        sql = readme + process.stdout.decode('utf-8')
        sql = sql.replace('SET row_security = off;\n', '')

        # create truncate tables sql code for all tables and add it to sql
        truncate_tables_sql = '\n'.join([
            'TRUNCATE %s CASCADE;' % item[0]
            for item in self.db.get_table_list()
        ])
        sql = re.sub(r'(SET search_path = public, pg_catalog;\n)',
                     '\\1BEGIN TRANSACTION;\n%s\n\n' % truncate_tables_sql,
                     sql)
        sql += 'COMMIT;\n'

        with open(os.path.join(SQL_DATA_PATH, output_name), 'w') as fw:
            fw.write(sql)

    # -------------------------------------------------
    # --- Scripts for generating listings SQL files ---
    # -------------------------------------------------
    def test_generate_listings_02_sql(self):
        etsy_test_case = 'listings_02'
        output_name = 'listings_02.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 6

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)

        self.save_db_dump(output_name)

    def test_generate_listings_03_sql(self):
        etsy_test_case = 'listings_03'
        output_name = 'listings_03.sql'

        self.import_shop(etsy_test_case, timeout_sec=180)

        assert self.db.get_number_of_products(2) == 102

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)

        self.save_db_dump(output_name)

    def test_generate_listings_09_sql(self):
        etsy_test_case = 'listings_09'
        output_name = 'listings_09.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 4

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)

        self.save_db_dump(output_name)

    def test_generate_listings_10_sql(self):
        etsy_test_case = 'listings_10'
        output_name = 'listings_10.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 1

        self.save_db_dump(output_name)

    def test_generate_listings_13_sql(self):
        etsy_test_case = 'listings_51'
        output_name = 'listings_13.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 3

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)

        self.save_db_dump(output_name)

    def test_generate_listings_14_sql(self):
        etsy_test_case = 'listings_14'
        output_name = 'listings_14.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 4

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)

        self.save_db_dump(output_name)

    def test_generate_listings_empty_sql(self):
        etsy_test_case = 'listings_empty'
        output_name = 'listings_empty.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 0

        self.save_db_dump(output_name)

    def test_generate_listings_15_sql(self):
        etsy_test_case = 'listings_15'
        output_name = 'listings_15.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 2

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)

        self.save_db_dump(output_name)

    def test_generate_listings_16_changed_sql(self):
        etsy_test_case = 'listings_16'
        output_name = 'listings_16_changed.sql'

        self.import_shop(etsy_test_case)

        assert self.db.get_number_of_products(2) == 3

        self.db.add_user_profile_flag(1, 'syncStatusModalSeen', True)
        self.db.change_can_write_inventory(True, '100003')

        self.save_db_dump(output_name)