def test_simple_json_with_overrides_get(self):
        # * Harold wants to use Dirigible to run his spreadsheets using
        #   a json-based rest API, and override the formula of some cells

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()

        # * He enables json api access for the sheet
        self.enable_json_api_for_sheet()

        # * He enters some values and formulae
        self.enter_cell_text(1, 1, "5")  # A1
        self.enter_cell_text(1, 2, "abc")  # A2
        self.enter_cell_text(2, 1, "6")  # B1
        self.enter_cell_text(3, 1, "=a1 + b1")  # C1
        self.wait_for_cell_value(3, 1, "11")

        # * He uses an API call to get the sheet as JSON
        #   but overrides the values of cells using GET params:
        #       B1=11
        #       C2=A1 + 1
        #   this also causes cell C1 to change value, since it
        #   depends on B1
        get_params = urlencode({"2,1": "11", "C2": "=A1 + 1", "api_key": self.get_my_username()})
        url = "%s?%s" % (Url.api_url(self.get_my_username(), sheet_id), get_params)
        try:
            sheet_content = json.load(urlopen(url))
        except Exception, e:
            self.fail(str(e))
Exemple #2
0
    def test_simple_json_with_overrides_get(self):
        # * Harold wants to use Dirigible to run his spreadsheets using
        #   a json-based rest API, and override the formula of some cells

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()

        # * He enables json api access for the sheet
        self.enable_json_api_for_sheet()

        # * He enters some values and formulae
        self.enter_cell_text(1, 1, '5')         # A1
        self.enter_cell_text(1, 2, 'abc')       # A2
        self.enter_cell_text(2, 1, '6')         # B1
        self.enter_cell_text(3, 1, '=a1 + b1')  # C1
        self.wait_for_cell_value(3, 1, '11')

        # * He uses an API call to get the sheet as JSON
        #   but overrides the values of cells using GET params:
        #       B1=11
        #       C2=A1 + 1
        #   this also causes cell C1 to change value, since it
        #   depends on B1
        get_params = urlencode({'2,1':'11', 'C2':'=A1 + 1', 'api_key': self.get_my_username()})
        url = '%s?%s' % (Url.api_url(self.get_my_username(), sheet_id), get_params)
        try:
            sheet_content = json.load(urlopen(url))
        except Exception, e:
            self.fail(str(e))
Exemple #3
0
    def test_simple_json_with_error(self):
        # * Harold wants to use Dirigible to run his spreadsheets using
        #   a json-based rest API, and wants the error-handling to be
        #   well-defined (if perhaps not ideal from a debugging perspective)


        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()

        # * He enables json api access for the sheet
        self.enable_json_api_for_sheet()

        # * He enters a single formula operating on a single value
        self.enter_cell_text(1, 1, '5')
        self.enter_cell_text(1, 2, '=1/A1')
        self.wait_for_cell_value(1, 2, '0.2')

        # * He uses an API call to get the sheet as JSON, passing in an override
        #   value that he knows will cause an error
        get_params = urlencode({'A1':'0', 'api_key': self.get_my_username()})
        url = '%s?%s' % (Url.api_url(self.get_my_username(), sheet_id), get_params)
        try:
            sheet_content = json.load(urlopen(url))
        except Exception, e:
            self.fail(str(e))
Exemple #4
0
    def test_simple_json(self):
        # * Harold wants to use Dirigible to run his spreadsheets using
        #   a json-based rest API

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()

        # * He enables json api access for the sheet
        self.enable_json_api_for_sheet()

        # * He enters some values and formulae
        self.enter_cell_text(1, 1, '5')
        self.enter_cell_text(1, 2, 'abc')
        self.enter_cell_text(2, 1, '6')
        self.enter_cell_text(3, 1, '=a1 + b1')
        self.wait_for_cell_value(3, 1, '11')
        # * He uses an API call to get the sheet as JSON
        sheet_content = json.load(urlopen(Url.api_url(self.get_my_username(), sheet_id), data=urlencode({'api_key': self.get_my_username()})))

        expected = {
            'name' : 'Sheet %s' % (sheet_id,),
            '1': {
                '1': 5,
                '2': 'abc'
                },
            '2': {
                '1': 6,
                },
            '3': {
                '1': 11
            },
        }
        self.assertEquals(sheet_content, expected)
Exemple #5
0
    def assert_copy_and_paste(self,
                              operation,
                              source_sheet,
                              dest_sheet=None,
                              dest_location=None,
                              to_set='formula'):
        if dest_sheet is None:
            dest_sheet = source_sheet

        # he populates some cells using usercode (because the test runs faster)
        orig_usercode = self.get_usercode()
        self.prepend_usercode(
            dedent('''
            for row in range(3, 6):
                for col in 'BC':
                    worksheet[col, row].%s = '%%s%%d' %% (col, row)
        ''' % (to_set, )))
        self.wait_for_cell_value(2, 3, 'B3')

        if to_set == 'formula':
            self.enter_usercode(orig_usercode)
            self.wait_for_spinner_to_stop()

        # he copies (or cuts) a region from one place
        operation((2, 3), (3, 5))

        # If he's cutting, it disappears
        if operation == self.cut_range:
            self.wait_for_cell_value(2, 3, '')
            self.wait_for_cell_value(3, 3, '')
            self.wait_for_cell_value(2, 4, '')
            self.wait_for_cell_value(3, 4, '')
            self.wait_for_cell_value(2, 5, '')
            self.wait_for_cell_value(3, 5, '')

        # ...and pastes it elsewhere
        if dest_sheet != source_sheet:
            self.go_to_url(Url.sheet_page(self.get_my_username(), dest_sheet))
            self.wait_for_grid_to_appear()

        self.paste_range(dest_location)

        # the destination is populated
        c, r = dest_location
        self.wait_for_cell_value(c, r, 'B3')
        self.wait_for_cell_value(1 + c, r, 'C3')
        self.wait_for_cell_value(c, 1 + r, 'B4')
        self.wait_for_cell_value(1 + c, 1 + r, 'C4')
        self.wait_for_cell_value(c, 2 + r, 'B5')
        self.wait_for_cell_value(1 + c, 2 + r, 'C5')
    def assert_copy_and_paste(self, operation, source_sheet, dest_sheet=None, dest_location=None, to_set="formula"):
        if dest_sheet is None:
            dest_sheet = source_sheet

        # he populates some cells using usercode (because the test runs faster)
        orig_usercode = self.get_usercode()
        self.prepend_usercode(
            dedent(
                """
            for row in range(3, 6):
                for col in 'BC':
                    worksheet[col, row].%s = '%%s%%d' %% (col, row)
        """
                % (to_set,)
            )
        )
        self.wait_for_cell_value(2, 3, "B3")

        if to_set == "formula":
            self.enter_usercode(orig_usercode)
            self.wait_for_spinner_to_stop()

        # he copies (or cuts) a region from one place
        operation((2, 3), (3, 5))

        # If he's cutting, it disappears
        if operation == self.cut_range:
            self.wait_for_cell_value(2, 3, "")
            self.wait_for_cell_value(3, 3, "")
            self.wait_for_cell_value(2, 4, "")
            self.wait_for_cell_value(3, 4, "")
            self.wait_for_cell_value(2, 5, "")
            self.wait_for_cell_value(3, 5, "")

        # ...and pastes it elsewhere
        if dest_sheet != source_sheet:
            self.go_to_url(Url.sheet_page(self.get_my_username(), dest_sheet))
            self.wait_for_grid_to_appear()

        self.paste_range(dest_location)

        # the destination is populated
        c, r = dest_location
        self.wait_for_cell_value(c, r, "B3")
        self.wait_for_cell_value(1 + c, r, "C3")
        self.wait_for_cell_value(c, 1 + r, "B4")
        self.wait_for_cell_value(1 + c, 1 + r, "C4")
        self.wait_for_cell_value(c, 2 + r, "B5")
        self.wait_for_cell_value(1 + c, 2 + r, "C5")
    def test_copy_and_paste_to_new_sheet(self):
        # * Harold logs in to Dirigible and creates a new sheet
        dest_sheet = self.login_and_create_new_sheet()
        source_sheet = self.create_new_sheet()

        self.assert_copy_and_paste(self.copy_range, source_sheet, dest_sheet, dest_location=(3, 4))

        # the cells in the original sheet are all still there
        self.go_to_url(Url.sheet_page(self.get_my_username(), source_sheet))
        self.wait_for_grid_to_appear()
        self.wait_for_cell_value(2, 3, "B3")
        self.wait_for_cell_value(2, 4, "B4")
        self.wait_for_cell_value(2, 5, "B5")
        self.wait_for_cell_value(3, 3, "C3")
        self.wait_for_cell_value(3, 4, "C4")
        self.wait_for_cell_value(3, 5, "C5")
    def test_access_sheet_with_incorrect_user_id(self):
        ## Create sheet as user 1, for the rest of the test
        harriet = self.get_my_usernames()[1]
        harold = self.get_my_username()
        sheet_id = self.login_and_create_new_sheet(username=harriet)
        self.logout()
        harolds_broken_sheet_url = Url.sheet_page(harold, sheet_id)

        # Before logging in, Harold tries to access one of Harriet's sheets
        # using the wrong direct URL, with his username but the correct sheet ID.
        # He gets a 404
        self.assert_HTTP_error(harolds_broken_sheet_url, 404)

        # After logging in, Harold tries to access one of Harriet's sheets
        # using the wrong direct URL, with his username but the correct sheet ID.
        # He gets a 404.
        self.login(username=harold)
        self.assert_HTTP_error(harolds_broken_sheet_url, 404)
Exemple #9
0
    def test_access_sheet_with_incorrect_user_id(self):
        ## Create sheet as user 1, for the rest of the test
        harriet = self.get_my_usernames()[1]
        harold = self.get_my_username()
        sheet_id = self.login_and_create_new_sheet(username=harriet)
        self.logout()
        harolds_broken_sheet_url = Url.sheet_page(harold, sheet_id)

        # Before logging in, Harold tries to access one of Harriet's sheets
        # using the wrong direct URL, with his username but the correct sheet ID.
        # He gets a 404
        self.assert_HTTP_error(harolds_broken_sheet_url, 404)

        # After logging in, Harold tries to access one of Harriet's sheets
        # using the wrong direct URL, with his username but the correct sheet ID.
        # He gets a 404.
        self.login(username=harold)
        self.assert_HTTP_error(harolds_broken_sheet_url, 404)
Exemple #10
0
    def test_copy_and_paste_to_new_sheet(self):
        # * Harold logs in to Dirigible and creates a new sheet
        dest_sheet = self.login_and_create_new_sheet()
        source_sheet = self.create_new_sheet()

        self.assert_copy_and_paste(self.copy_range,
                                   source_sheet,
                                   dest_sheet,
                                   dest_location=(3, 4))

        # the cells in the original sheet are all still there
        self.go_to_url(Url.sheet_page(self.get_my_username(), source_sheet))
        self.wait_for_grid_to_appear()
        self.wait_for_cell_value(2, 3, 'B3')
        self.wait_for_cell_value(2, 4, 'B4')
        self.wait_for_cell_value(2, 5, 'B5')
        self.wait_for_cell_value(3, 3, 'C3')
        self.wait_for_cell_value(3, 4, 'C4')
        self.wait_for_cell_value(3, 5, 'C5')
    def test_simple_json(self):
        # * Harold wants to use Dirigible to run his spreadsheets using
        #   a json-based rest API

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()

        # * He enables json api access for the sheet
        self.enable_json_api_for_sheet()

        # * He enters some values and formulae
        self.enter_cell_text(1, 1, "5")
        self.enter_cell_text(1, 2, "abc")
        self.enter_cell_text(2, 1, "6")
        self.enter_cell_text(3, 1, "=a1 + b1")
        self.wait_for_cell_value(3, 1, "11")
        # * He uses an API call to get the sheet as JSON
        sheet_content = json.load(
            urlopen(Url.api_url(self.get_my_username(), sheet_id), data=urlencode({"api_key": self.get_my_username()}))
        )

        expected = {"name": "Sheet %s" % (sheet_id,), "1": {"1": 5, "2": "abc"}, "2": {"1": 6}, "3": {"1": 11}}
        self.assertEquals(sheet_content, expected)
    def test_simple_json_with_error(self):
        # * Harold wants to use Dirigible to run his spreadsheets using
        #   a json-based rest API, and wants the error-handling to be
        #   well-defined (if perhaps not ideal from a debugging perspective)

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()

        # * He enables json api access for the sheet
        self.enable_json_api_for_sheet()

        # * He enters a single formula operating on a single value
        self.enter_cell_text(1, 1, "5")
        self.enter_cell_text(1, 2, "=1/A1")
        self.wait_for_cell_value(1, 2, "0.2")

        # * He uses an API call to get the sheet as JSON, passing in an override
        #   value that he knows will cause an error
        get_params = urlencode({"A1": "0", "api_key": self.get_my_username()})
        url = "%s?%s" % (Url.api_url(self.get_my_username(), sheet_id), get_params)
        try:
            sheet_content = json.load(urlopen(url))
        except Exception, e:
            self.fail(str(e))
    def test_json_api_auth(self):
        # Harold wants to make sure that people only have JSON access to his sheets
        # when he has explicitly granted it.

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()
        base_json_url = urljoin(self.browser.current_url, 'v0.1/json/')

        # * He enters some values and formulae
        self.enter_cell_text(1, 1, '5')

        # * He tries to use an API call to get the sheet as JSON, but gets a 403 error.
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url)
        self.assertEquals(mngr.exception.code, 403)

        # * Looking around at the sheet page, he notices a "Security" button.
        self.wait_for_element_to_appear('id=id_security_button')

        # * He sees that the mouseover text on the button indicates that the JSON API is not enabled
        self.assertTrue(
            'JSON API disabled' in
            self.selenium.get_attribute('css=#id_security_button@title')
        )
        self.assertTrue(
            'JSON API disabled' in
            self.selenium.get_attribute('css=#id_security_button@alt')
        )

        # * He clicks the button.
        self.selenium.click('id=id_security_button')

        # * A dialog appears; there is an unchecked toggle saying "Allow JSON API access"
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error', False)
        self.wait_for_element_visibility('id=id_security_form_json_enabled_checkbox', True)
        self.wait_for_element_visibility('id=id_security_form_json_api_key', True)
        self.wait_for_element_visibility('id=id_security_form_json_api_url', True)

        self.assertFalse(self.is_element_enabled('id_security_form_json_api_key'))
        self.assertFalse(self.is_element_enabled('id_security_form_json_api_url'))

        self.assertEquals(
            self.get_text('css=label[for="id_security_form_json_enabled_checkbox"]'),
            'Allow JSON API access'
        )
        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'off')

        # * ... and OK and Cancel buttons
        self.wait_for_element_visibility('id=id_security_form_ok_button', True)
        self.wait_for_element_visibility('id=id_security_form_cancel_button', True)

        # * He checks it.  He notices a textbox giving him an "API key",
        self.selenium.click('id=id_security_form_json_enabled_checkbox')
        self.assertTrue(self.is_element_enabled('id_security_form_json_api_key'))
        api_key = self.selenium.get_value('id=id_security_form_json_api_key')
        api_url = self.selenium.get_value('id=id_security_form_json_api_url')

        # * He also notices that when he clicks on the URL text field, the entire field is selected
        ## The focus call is to appease Chrome
        self.selenium.focus('id=id_security_form_json_api_url')
        self.selenium.click('id=id_security_form_json_api_url')

        # our 'caret' plugin appears to have a problem getting the selection
        # range for fields that are not editable, such as the json api url.
        # Consequently, we have to check the selection by copying this
        # text, and checking the clipboard content.
        with self.key_down(key_codes.CTRL):
            self.human_key_press(key_codes.LETTER_C)

        def get_clipboard_text():
            OpenClipboard()
            text = GetClipboardData(win32con.CF_TEXT)
            CloseClipboard()
            return text

        self.wait_for(
            lambda: get_clipboard_text() == api_url,
            lambda: 'bad clipboard text, was: %s' % (get_clipboard_text(),)
        )

        # * nothing appears outside the JSON API dialog box yet though.
        self.assertTrue(
            'JSON API disabled' in
            self.selenium.get_attribute('css=#id_security_button@title')
        )
        self.assertTrue(
            'JSON API disabled' in
            self.selenium.get_attribute('css=#id_security_button@alt')
        )

        # * He ignores all of the key stuff, presses Cancel
        self.selenium.click('id=id_security_form_cancel_button')

        # * He notices that the form disappears and that the icon still indicates that the JSON API is disabled
        self.wait_for_element_visibility('id=id_security_form', False)
        self.assertTrue(
            'JSON API disabled' in
            self.selenium.get_attribute('css=#id_security_button@title')
        )
        self.assertTrue(
            'JSON API disabled' in
            self.selenium.get_attribute('css=#id_security_button@alt')
        )

        # but he just tries accessing the JSON URL without a key again
        # * He gets 403 again.
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url)
        self.assertEquals(mngr.exception.code, 403)

        # * and he also gets 403 when he uses the API Key that was displayed
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': api_key}))
        self.assertEquals(mngr.exception.code, 403)

        # * He half-realises what the problem is, opens the dialog again, checks the box, and presses OK
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error', False)
        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'off')
        self.selenium.click('id=id_security_form_json_enabled_checkbox')
        self.assertTrue(self.is_element_enabled('id_security_form_json_api_key'))
        self.assertTrue(self.is_element_enabled('id_security_form_json_api_url'))
        api_url = self.selenium.get_value('css=#id_security_form_json_api_url')
        self.selenium.click('id=id_security_form_ok_button')
        self.wait_for_element_visibility('id=id_security_form', False)

        #* He now sees the toolbar indicates that the JSON API is enabled for this sheet
        self.assertTrue(
            'JSON API enabled' in
            self.selenium.get_attribute('css=#id_security_button@title')
        )
        self.assertTrue(
            'JSON API enabled' in
            self.selenium.get_attribute('css=#id_security_button@alt')
        )

        # * Not trusting the memory of his browser, he opens the dialog again
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error', False)
        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'on')

        # * and immediately presses Cancel
        self.selenium.click('id=id_security_form_cancel_button')

        # * He is surprised and delighted to see that his sheet is still JSON-enabled
        self.assertTrue(
            'JSON API enabled' in
            self.selenium.get_attribute('css=#id_security_button@title')
        )
        self.assertTrue(
            'JSON API enabled' in
            self.selenium.get_attribute('css=#id_security_button@alt')
        )

        expected_url = "%s%s?api_key=%s" % (
            self.selenium.browserURL[:-1],
            urlparse(Url.api_url(self.get_my_username(), sheet_id)).path,
            api_key
        )
        self.assertEquals(api_url, expected_url)

        # .. despite this helpful link, he tries again with the wrong API key
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': 'abcd1234-123dfe'}))
        # * He gets a 403
        self.assertEquals(mngr.exception.code, 403)


        # * Frustrated, he tries again with the right API key.
        response = urlopen(base_json_url, urlencode({'api_key': api_key}))

        # * He gets the data he expected.
        json_data = json.load(response)
        self.assertEquals(json_data['1']['1'], 5)

        # * He changes the API key in the JSON API dialog.
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error', False)
        old_api_url = self.selenium.get_value('css=#id_security_form_json_api_url')
        self.selenium.type('id=id_security_form_json_api_key', 'some_new_api_ke')
        self.selenium.focus('id=id_security_form_json_api_key')

        # He sees that the api url is updated with every keystroke
        self.human_key_press(key_codes.END) # Move IE insert point to the end
        self.human_key_press(key_codes.LETTER_Y)

        self.assertEquals(
            self.selenium.get_value('css=#id_security_form_json_api_url'),
            old_api_url.replace(api_key, 'some_new_api_key')
        )
        self.selenium.click('id=id_security_form_ok_button')
        self.wait_for_element_visibility('id=id_security_form', False)

        # * He tries again, using the old key
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': api_key}))
        # * He gets a 403
        self.assertEquals(mngr.exception.code, 403)

        # * He tries using the right key.
        response = urlopen(base_json_url, urlencode({'api_key': 'some_new_api_key'}))

        # * It works.
        json_data = json.load(response)
        self.assertEquals(json_data['1']['1'], 5)

        # * He refreshes the sheet page
        self.refresh_sheet_page()

        # * and notes that his setting has been remembered by the server
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error', False)
        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'on')

        # * He makes the sheet private again.
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error', False)
        self.selenium.click('id=id_security_form_json_enabled_checkbox')
        self.selenium.click('id=id_security_form_ok_button')
        self.wait_for_element_visibility('id=id_security_form', False)

        # * He tries with the key that worked last time.
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': 'some_new_api_key'}))
        # * He gets a 403
        self.assertEquals(mngr.exception.code, 403)
Exemple #14
0
    def test_json_api_auth(self):
        # Harold wants to make sure that people only have JSON access to his sheets
        # when he has explicitly granted it.

        # * He logs in to Dirigible and creates a new sheet
        sheet_id = self.login_and_create_new_sheet()
        base_json_url = urljoin(self.browser.current_url, 'v0.1/json/')

        # * He enters some values and formulae
        self.enter_cell_text(1, 1, '5')

        # * He tries to use an API call to get the sheet as JSON, but gets a 403 error.
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url)
        self.assertEquals(mngr.exception.code, 403)

        # * Looking around at the sheet page, he notices a "Security" button.
        self.wait_for_element_to_appear('id=id_security_button')

        # * He sees that the mouseover text on the button indicates that the JSON API is not enabled
        self.assertTrue('JSON API disabled' in self.selenium.get_attribute(
            'css=#id_security_button@title'))
        self.assertTrue('JSON API disabled' in self.selenium.get_attribute(
            'css=#id_security_button@alt'))

        # * He clicks the button.
        self.selenium.click('id=id_security_button')

        # * A dialog appears; there is an unchecked toggle saying "Allow JSON API access"
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error',
                                         False)
        self.wait_for_element_visibility(
            'id=id_security_form_json_enabled_checkbox', True)
        self.wait_for_element_visibility('id=id_security_form_json_api_key',
                                         True)
        self.wait_for_element_visibility('id=id_security_form_json_api_url',
                                         True)

        self.assertFalse(
            self.is_element_enabled('id_security_form_json_api_key'))
        self.assertFalse(
            self.is_element_enabled('id_security_form_json_api_url'))

        self.assertEquals(
            self.get_text(
                'css=label[for="id_security_form_json_enabled_checkbox"]'),
            'Allow JSON API access')
        self.assertEquals(
            self.selenium.get_value(
                'id=id_security_form_json_enabled_checkbox'), 'off')

        # * ... and OK and Cancel buttons
        self.wait_for_element_visibility('id=id_security_form_ok_button', True)
        self.wait_for_element_visibility('id=id_security_form_cancel_button',
                                         True)

        # * He checks it.  He notices a textbox giving him an "API key",
        self.selenium.click('id=id_security_form_json_enabled_checkbox')
        self.assertTrue(
            self.is_element_enabled('id_security_form_json_api_key'))
        api_key = self.selenium.get_value('id=id_security_form_json_api_key')
        api_url = self.selenium.get_value('id=id_security_form_json_api_url')

        # * He also notices that when he clicks on the URL text field, the entire field is selected
        ## The focus call is to appease Chrome
        self.selenium.focus('id=id_security_form_json_api_url')
        self.selenium.click('id=id_security_form_json_api_url')

        # our 'caret' plugin appears to have a problem getting the selection
        # range for fields that are not editable, such as the json api url.
        # Consequently, we have to check the selection by copying this
        # text, and checking the clipboard content.
        with self.key_down(key_codes.CTRL):
            self.human_key_press(key_codes.LETTER_C)

        def get_clipboard_text():
            OpenClipboard()
            text = GetClipboardData(win32con.CF_TEXT)
            CloseClipboard()
            return text

        self.wait_for(
            lambda: get_clipboard_text() == api_url,
            lambda: 'bad clipboard text, was: %s' % (get_clipboard_text(), ))

        # * nothing appears outside the JSON API dialog box yet though.
        self.assertTrue('JSON API disabled' in self.selenium.get_attribute(
            'css=#id_security_button@title'))
        self.assertTrue('JSON API disabled' in self.selenium.get_attribute(
            'css=#id_security_button@alt'))

        # * He ignores all of the key stuff, presses Cancel
        self.selenium.click('id=id_security_form_cancel_button')

        # * He notices that the form disappears and that the icon still indicates that the JSON API is disabled
        self.wait_for_element_visibility('id=id_security_form', False)
        self.assertTrue('JSON API disabled' in self.selenium.get_attribute(
            'css=#id_security_button@title'))
        self.assertTrue('JSON API disabled' in self.selenium.get_attribute(
            'css=#id_security_button@alt'))

        # but he just tries accessing the JSON URL without a key again
        # * He gets 403 again.
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url)
        self.assertEquals(mngr.exception.code, 403)

        # * and he also gets 403 when he uses the API Key that was displayed
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': api_key}))
        self.assertEquals(mngr.exception.code, 403)

        # * He half-realises what the problem is, opens the dialog again, checks the box, and presses OK
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error',
                                         False)
        self.assertEquals(
            self.selenium.get_value(
                'id=id_security_form_json_enabled_checkbox'), 'off')
        self.selenium.click('id=id_security_form_json_enabled_checkbox')
        self.assertTrue(
            self.is_element_enabled('id_security_form_json_api_key'))
        self.assertTrue(
            self.is_element_enabled('id_security_form_json_api_url'))
        api_url = self.selenium.get_value('css=#id_security_form_json_api_url')
        self.selenium.click('id=id_security_form_ok_button')
        self.wait_for_element_visibility('id=id_security_form', False)

        #* He now sees the toolbar indicates that the JSON API is enabled for this sheet
        self.assertTrue('JSON API enabled' in self.selenium.get_attribute(
            'css=#id_security_button@title'))
        self.assertTrue('JSON API enabled' in self.selenium.get_attribute(
            'css=#id_security_button@alt'))

        # * Not trusting the memory of his browser, he opens the dialog again
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error',
                                         False)
        self.assertEquals(
            self.selenium.get_value(
                'id=id_security_form_json_enabled_checkbox'), 'on')

        # * and immediately presses Cancel
        self.selenium.click('id=id_security_form_cancel_button')

        # * He is surprised and delighted to see that his sheet is still JSON-enabled
        self.assertTrue('JSON API enabled' in self.selenium.get_attribute(
            'css=#id_security_button@title'))
        self.assertTrue('JSON API enabled' in self.selenium.get_attribute(
            'css=#id_security_button@alt'))

        expected_url = "%s%s?api_key=%s" % (
            self.selenium.browserURL[:-1],
            urlparse(Url.api_url(self.get_my_username(),
                                 sheet_id)).path, api_key)
        self.assertEquals(api_url, expected_url)

        # .. despite this helpful link, he tries again with the wrong API key
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': 'abcd1234-123dfe'}))
        # * He gets a 403
        self.assertEquals(mngr.exception.code, 403)

        # * Frustrated, he tries again with the right API key.
        response = urlopen(base_json_url, urlencode({'api_key': api_key}))

        # * He gets the data he expected.
        json_data = json.load(response)
        self.assertEquals(json_data['1']['1'], 5)

        # * He changes the API key in the JSON API dialog.
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error',
                                         False)
        old_api_url = self.selenium.get_value(
            'css=#id_security_form_json_api_url')
        self.selenium.type('id=id_security_form_json_api_key',
                           'some_new_api_ke')
        self.selenium.focus('id=id_security_form_json_api_key')

        # He sees that the api url is updated with every keystroke
        self.human_key_press(key_codes.END)  # Move IE insert point to the end
        self.human_key_press(key_codes.LETTER_Y)

        self.assertEquals(
            self.selenium.get_value('css=#id_security_form_json_api_url'),
            old_api_url.replace(api_key, 'some_new_api_key'))
        self.selenium.click('id=id_security_form_ok_button')
        self.wait_for_element_visibility('id=id_security_form', False)

        # * He tries again, using the old key
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': api_key}))
        # * He gets a 403
        self.assertEquals(mngr.exception.code, 403)

        # * He tries using the right key.
        response = urlopen(base_json_url,
                           urlencode({'api_key': 'some_new_api_key'}))

        # * It works.
        json_data = json.load(response)
        self.assertEquals(json_data['1']['1'], 5)

        # * He refreshes the sheet page
        self.refresh_sheet_page()

        # * and notes that his setting has been remembered by the server
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error',
                                         False)
        self.assertEquals(
            self.selenium.get_value(
                'id=id_security_form_json_enabled_checkbox'), 'on')

        # * He makes the sheet private again.
        self.selenium.click('id=id_security_button')
        self.wait_for_element_visibility('id=id_security_form', True)
        self.wait_for_element_visibility('id=id_security_form_save_error',
                                         False)
        self.selenium.click('id=id_security_form_json_enabled_checkbox')
        self.selenium.click('id=id_security_form_ok_button')
        self.wait_for_element_visibility('id=id_security_form', False)

        # * He tries with the key that worked last time.
        with self.assertRaises(HTTPError) as mngr:
            urlopen(base_json_url, urlencode({'api_key': 'some_new_api_key'}))
        # * He gets a 403
        self.assertEquals(mngr.exception.code, 403)