Well the idea is pretty simple, while you write specification in Gherkin you describe the way you want the application to behave.
Writing the documentation is explaining how the application behaves so as to learn how to use it.
What if from good requirements we could infer part of the documentation ?
If we run these tests with behave and run a selenium driver we can leverage to take a few screenshots and add information about what's happening. In short we want to go from
Feature: Devpi server list our personal packages As a Developer I want to access the in house package repository So that I can develop the latest and greatest software Scenario: see my personal index Given I access the main devpi page as "nlaurance" When I click My personal repository Then I have permission to upload packages And I want a "overlined" screenshot saved as "index.png" And I document in the "se" as "bubble_list.png" with """ Make sure the upload permission is set for you """
to
let's have a peek at what's in devpi.py, inside the steps directory
def dom_element_size(dom_element): x, y = dom_element.location['x'], dom_element.location['y'] width, height = dom_element.size['width'], dom_element.size['height'] return x, y, width, height @then(u'I have permission to upload packages') def step_impl(context): permissions = context.browser.find_element_by_class_name('permissions') assert_that(permissions.text, contains_string(context.user)) my_permission = permissions.find_element_by_xpath("descendant::span[contains(text(), '{0}')]".format(context.devpi_user)) outline_elements = getattr(context, 'outline_elements', []) outline_elements.append(dom_element_size(my_permission)) context.outline_elements = outline_elements
at this stage there is a hamcrest assertion, to make sure we comply to the requirement. Then we keep track of the desired element(s) position and size.
@then(u'I document in the "{hotspot}" as "{filename}" with') def step_impl(context, hotspot, filename): outline_elements = getattr(context, 'outline_elements', []) screenshot = getattr(context, 'current_screenshot', StringIO(context.browser.get_screenshot_as_png())) commented = paste_bubble(screenshot, outline_elements, hotspot, context.text, filename) context.current_screenshot = commented
We can take a screenshot at any time, to allow chaining we pass the current screenshot to the context.
so what is paste_bubble doing ?
it uses the ninepatch library to format image ressources to our need. In fact a fork of it to simplify the code.
def paste_bubble(screenshot, coords, hotspot, text, save_as='screenshot.png'): image = screenshot if isinstance(screenshot, Image.Image) else Image.open(screenshot) for coord in coords: x, y, width, height = coord # se paste_x = int(x + width) paste_y = int(y + height) bubble_img = bubble(text, hotspot) image.paste(bubble_img, (paste_x, paste_y), bubble_img) image.save(save_as) return image
Yes, this is still at the embryo stage, and the hotspot (position) is not really implemented yet.
def bubble(text, hotspot="se"): ninepatch = Ninepatch('./bubbles/{0}.9.png'.format(hotspot)) text_as_image = text2img(text) scaled_bubble = ninepatch.render_with_content(text_as_image) if DEBUG: scaled_bubble.save('bubble.png') return scaled_bubble
Now some play with inkscape and nine patch editor, to create some outlines. In here we have a se.9.png file in the bubbles subdirectory.
This is a work in progress, but that first result is rewarding enough.
I'll see if this deserves a packaging of its own.
No comments:
Post a Comment