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))
Example #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))
Example #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))
Example #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)
    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)
Example #8
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)