Ejemplo n.º 1
0
def define_keyboard():
    kb = stbt.Keyboard()  # pylint:disable=redefined-outer-name
    for mode in ["lowercase", "uppercase", "symbols"]:
        kb.add_grid(top_grid, mode=mode)
        kb.add_grid(bottom_grid, mode=mode)
        g = middle_grids[mode]
        kb.add_grid(g, mode=mode)

        # Transitions between grids:
        #
        # abc ABC #+-  (top grid)
        # ↕ ↕ ↕ ↕ ↕ ↕
        # a b c d e f  (first row of middle grid)
        kb.add_transition(g[0, 0].data, "abc", "KEY_UP", mode=mode)
        kb.add_transition(g[1, 0].data, "abc", "KEY_UP", mode=mode)
        kb.add_transition(g[2, 0].data, "ABC", "KEY_UP", mode=mode)
        kb.add_transition(g[3, 0].data, "ABC", "KEY_UP", mode=mode)
        kb.add_transition(g[4, 0].data, "#+-", "KEY_UP", mode=mode)
        kb.add_transition(g[5, 0].data, "#+-", "KEY_UP", mode=mode)

        # 5 6 7 8 9 0  (last row of middle grid)
        # ↕ ↕ ↕ ↕ ↕ ↕
        # SPC DEL CLR  (bottom grid)
        kb.add_transition(g[0, 5].data, " ", "KEY_DOWN", mode=mode)
        kb.add_transition(g[1, 5].data, " ", "KEY_DOWN", mode=mode)
        kb.add_transition(g[2, 5].data, "DELETE", "KEY_DOWN", mode=mode)
        kb.add_transition(g[3, 5].data, "DELETE", "KEY_DOWN", mode=mode)
        kb.add_transition(g[4, 5].data, "CLEAR", "KEY_DOWN", mode=mode)
        kb.add_transition(g[5, 5].data, "CLEAR", "KEY_DOWN", mode=mode)

    # Transitions between modes:
    for source_mode in ["lowercase", "uppercase", "symbols"]:
        for name, target_mode in [("abc", "lowercase"),
                                  ("ABC", "uppercase"),
                                  ("#+-", "symbols")]:
            kb.add_transition(kb.find_key(name=name, mode=source_mode),
                              kb.find_key(name=name, mode=target_mode),
                              "KEY_OK")

    # Pressing KEY_PLAY cycles between the modes
    for source_mode, target_mode in [
            ("lowercase", "uppercase"),
            ("symbols", "lowercase"),
            # Pressing KEY_PLAY from "uppercase" goes to "lowercase" if you
            # have already typed an uppercase letter since entering uppercase
            # mode; if you haven't, it goes to "symbols". We don't keep track
            # of this past state in our model, so we model it as a
            # non-deterministic state machine -- that is, pressing KEY_PLAY
            # from "uppercase" might go to "symbols" or to "lowercase", we just
            # have to press it and then look at the screen to find out where we
            # landed.
            ("uppercase", "symbols"),
            ("uppercase", "lowercase")]:
        for key in kb.find_keys(mode=source_mode):
            target = kb.find_key(region=key.region, mode=target_mode)
            kb.add_transition(key, target, "KEY_PLAY")

    return kb
Ejemplo n.º 2
0
def test_keyboard_with_hash_sign():
    """Regression test. `networkx.parse_edgelist` treats "#" as a comment."""
    kb = stbt.Keyboard("""
        @hotmail.com !#$ KEY_DOWN
        @hotmail.com @ KEY_DOWN
        @ # KEY_RIGHT
        # @ KEY_LEFT
        L K KEY_LEFT
        K L KEY_RIGHT
    """)
    keys = list(_keys_to_press(kb.G, "@hotmail.com", "@"))
    assert keys == [('KEY_DOWN', {'@', '!#$'})]

    assert list(_keys_to_press(kb.G, "@", "#")) == [('KEY_RIGHT', {'#'})]
    assert list(_keys_to_press(kb.G, "#", "@")) == [('KEY_LEFT', {'@'})]

    # L is the first character of the random comments delimiter in
    # `Keyboard.parse_edgelist`. Check that networkx uses the whole string, not
    # just the first character.
    assert list(_keys_to_press(kb.G, "K", "L")) == [('KEY_RIGHT', {'L'})]
    assert list(_keys_to_press(kb.G, "L", "K")) == [('KEY_LEFT', {'K'})]
Ejemplo n.º 3
0
class _Keyboard(stbt.FrameObject):
    """Immutable FrameObject representing the test's view of the Device Under
    Test (``dut``).

    The keyboard looks like this::

        A  B  C  D  E  F  G
        H  I  J  K  L  M  N
        O  P  Q  R  S  T  U
        V  W  X  Y  Z  -  '
         SPACE  CLEAR  SEARCH

    """
    def __init__(self, dut):
        super(_Keyboard, self).__init__(
            frame=numpy.zeros((720, 1280, 3), dtype=numpy.uint8))
        self._dut = dut  # Device Under Test -- i.e. ``YouTubeKeyboard``
        self._selection = self._dut.selection

    @property
    def is_visible(self):
        return True

    @property
    def selection(self):
        return self._selection

    def refresh(self, frame=None, **kwargs):
        print("_Keyboard.refresh: Now on %r" % self._dut.selection)
        return _Keyboard(dut=self._dut)

    KEYBOARD = stbt.Keyboard(GRAPH, navigate_timeout=0.1)

    def enter_text(self, text):
        return self.KEYBOARD.enter_text(text.upper(), page=self)

    def navigate_to(self, target, verify_every_keypress=False):
        return self.KEYBOARD.navigate_to(
            target, page=self, verify_every_keypress=verify_every_keypress)
Ejemplo n.º 4
0
def test_composing_complex_keyboards():
    """The YouTube keyboard on Roku looks like this::

        A  B  C  D  E  F  G
        H  I  J  K  L  M  N
        O  P  Q  R  S  T  U
        V  W  X  Y  Z  -  '
         SPACE  CLEAR  SEARCH

    The first 4 rows behave normally within themselves. The bottom row behaves
    normally within itself. But navigating to or from the bottom row is a bit
    irregular: No matter what column you're in, when you press KEY_DOWN you
    always land on SPACE. Then when you press KEY_UP, you go back to the column
    you were last on -- even if you had pressed KEY_RIGHT/KEY_LEFT to move
    within the bottom row. It's almost like they're two separate state
    machines, and we can model them as such, with a few explicit connections
    between the two.
    """
    letters = stbt.Grid(stbt.Region(x=540, y=100, right=840, bottom=280),
                        data=["ABCDEFG",
                              "HIJKLMN",
                              "OPQRSTU",
                              "VWXYZ-'"])
    space_row = stbt.Grid(stbt.Region(x=540, y=280, right=840, bottom=330),
                          data=[[" ", "CLEAR", "SEARCH"]])

    # Technique #0: Write the entire edgelist manually (as per previous tests)
    K0 = stbt.Keyboard(GRAPH)

    # Technique #1: Manipulate the graph (manually or programmatically) directly
    G1 = nx.compose(stbt.grid_to_navigation_graph(letters),
                    stbt.grid_to_navigation_graph(space_row))
    # Pressing down from the bottom row always goes to SPACE:
    for k in letters.data[-1]:
        G1.add_edge(k, " ", key="KEY_DOWN")
    # Pressing back up from the space/clear/search row can go to any column
    # in the bottom row:
    for k in space_row.data[0]:
        for j in letters.data[-1]:
            G1.add_edge(k, j, key="KEY_UP")
    K1 = stbt.Keyboard(G1)

    assert sorted(K0.G.edges(data=True)) == sorted(K1.G.edges(data=True))

    # Technique #2: Use manually-written edgelist only for the irregular edges
    # Note that Keyboard.__init__ will normalise "SPACE" -> " " so it doesn't
    # matter if the 3 different graphs have different representations for
    # "SPACE".
    connections = stbt.Keyboard.parse_edgelist("""
        V SPACE KEY_DOWN
        W SPACE KEY_DOWN
        X SPACE KEY_DOWN
        Y SPACE KEY_DOWN
        Z SPACE KEY_DOWN
        - SPACE KEY_DOWN
        ' SPACE KEY_DOWN
        SPACE V KEY_UP
        SPACE W KEY_UP
        SPACE X KEY_UP
        SPACE Y KEY_UP
        SPACE Z KEY_UP
        SPACE - KEY_UP
        SPACE ' KEY_UP
        CLEAR V KEY_UP
        CLEAR W KEY_UP
        CLEAR X KEY_UP
        CLEAR Y KEY_UP
        CLEAR Z KEY_UP
        CLEAR - KEY_UP
        CLEAR ' KEY_UP
        SEARCH V KEY_UP
        SEARCH W KEY_UP
        SEARCH X KEY_UP
        SEARCH Y KEY_UP
        SEARCH Z KEY_UP
        SEARCH - KEY_UP
        SEARCH ' KEY_UP
    """)
    G2 = nx.compose_all([stbt.grid_to_navigation_graph(letters),
                         stbt.grid_to_navigation_graph(space_row),
                         connections])
    K2 = stbt.Keyboard(G2)

    assert sorted(K0.G.edges(data=True)) == sorted(K2.G.edges(data=True))