From fa90f078e5de5a1acbd9eda529db17ab1b2b4cb3 Mon Sep 17 00:00:00 2001 From: Sean Brant Date: Sat, 9 Mar 2013 15:57:26 -0600 Subject: Initial Import --- .gitignore | 4 ++ LICENSE | 12 ++++++ MANIFEST.in | 1 + Makefile | 14 +++++++ README.rst | 48 +++++++++++++++++++++ pykss/__init__.py | 1 + pykss/comment.py | 91 ++++++++++++++++++++++++++++++++++++++++ pykss/modifier.py | 9 ++++ pykss/parser.py | 37 ++++++++++++++++ pykss/section.py | 57 +++++++++++++++++++++++++ runtests.py | 18 ++++++++ setup.py | 27 ++++++++++++ tests/__init__.py | 0 tests/fixtures/comments.txt | 33 +++++++++++++++ tests/fixtures/css/buttons.css | 38 +++++++++++++++++ tests/fixtures/less/_label.less | 10 +++++ tests/fixtures/less/buttons.less | 51 ++++++++++++++++++++++ tests/fixtures/less/mixins.less | 8 ++++ tests/fixtures/less/nested.less | 17 ++++++++ tests/fixtures/sass/buttons.sass | 40 ++++++++++++++++++ tests/fixtures/sass/nested.sass | 10 +++++ tests/fixtures/scss/buttons.scss | 47 +++++++++++++++++++++ tests/fixtures/scss/nested.scss | 12 ++++++ tests/test_comment.py | 65 ++++++++++++++++++++++++++++ tests/test_modifier.py | 15 +++++++ tests/test_parser.py | 48 +++++++++++++++++++++ tests/test_section.py | 36 ++++++++++++++++ tox.ini | 11 +++++ 28 files changed, 760 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README.rst create mode 100644 pykss/__init__.py create mode 100644 pykss/comment.py create mode 100644 pykss/modifier.py create mode 100644 pykss/parser.py create mode 100644 pykss/section.py create mode 100644 runtests.py create mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/fixtures/comments.txt create mode 100644 tests/fixtures/css/buttons.css create mode 100644 tests/fixtures/less/_label.less create mode 100644 tests/fixtures/less/buttons.less create mode 100644 tests/fixtures/less/mixins.less create mode 100644 tests/fixtures/less/nested.less create mode 100644 tests/fixtures/sass/buttons.sass create mode 100644 tests/fixtures/sass/nested.sass create mode 100644 tests/fixtures/scss/buttons.scss create mode 100644 tests/fixtures/scss/nested.scss create mode 100644 tests/test_comment.py create mode 100644 tests/test_modifier.py create mode 100644 tests/test_parser.py create mode 100644 tests/test_section.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b2d879 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.egg-info +*.pyc +*.tox +__pycache__ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b6c76fc --- /dev/null +++ b/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2013 Sean Brant and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the PyKSS nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..9561fb1 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include README.rst diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..caa2f38 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +lint: + @echo "Linting Python files" + flake8 --ignore=E501,E225,E121,E123,E124,E125,E127,E128 pykss + @echo "" + +install-test-requirements: + pip install "file://`pwd`#egg=pykss[tests]" + +test-python: + @echo "Running Python tests" + python setup.py -q test || exit 1 + @echo "" + +test: install-test-requirements lint test-python diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..6e7ff2c --- /dev/null +++ b/README.rst @@ -0,0 +1,48 @@ +PyKSS - Knyle Style Sheets +========================== + +PyKSS is a Python implementation of KSS_. KSS attempts to provide a +methodology for writing maintainable, documented CSS within a team. + +The official docs_ provide a good introduction to KSS. The complete +syntax can he found on the syntax_ page. + +.. _KSS: http://warpspire.com/kss +.. _docs: http://warpspire.com/kss/ +.. _syntax: http://warpspire.com/kss/syntax/ + + +Installing +---------- + +.. code-block:: shell + + pip install pykss + + +Usage +----- + +.. code-block:: python + + >>> import pykss + >>> + >>> styleguide = pykss.Parser('static/css') + >>> + >>> styleguide.section('2.1.1') + + >>> + >>> styleguide.section('2.1.1').description + 'A button suitable for giving stars to someone.' + >>> + >>> styleguide.section('2.1.1').modifiers[0] + + >>> + >>> styleguide.section('2.1.1').modifiers[0].name + ':hover' + >>> + >>> styleguide.section('2.1.1').modifiers[0].class_name + 'pseudo-class-hover' + >>> + >>> styleguide.section('2.1.1').modifiers[0].description + 'Subtle hover highlight' diff --git a/pykss/__init__.py b/pykss/__init__.py new file mode 100644 index 0000000..126c07c --- /dev/null +++ b/pykss/__init__.py @@ -0,0 +1 @@ +from pykss.parser import Parser # NOQA diff --git a/pykss/comment.py b/pykss/comment.py new file mode 100644 index 0000000..69e148b --- /dev/null +++ b/pykss/comment.py @@ -0,0 +1,91 @@ +SINGLE_LINE = '//' +MULTI_LINE = '*' +MULTI_LINE_START = '/*' +MULTI_LINE_END = '*/' + + +def is_single_line_comment(line): + return line.startswith(SINGLE_LINE) + + +def is_multi_line_comment_start(line): + return line.startswith(MULTI_LINE_START) + + +def is_multi_line_comment_end(line): + if is_single_line_comment(line): + return False + return line.endswith(MULTI_LINE_END) + + +def parse_single_line(line): + return line[len(SINGLE_LINE):].strip() + + +def parse_multi_line(line): + if is_multi_line_comment_start(line): + line = line[len(MULTI_LINE_START):] + + if is_multi_line_comment_end(line): + line = line[:-len(MULTI_LINE_END)] + + if line.startswith(MULTI_LINE): + line = line[len(MULTI_LINE):] + + return line.strip() + + +def normalize(lines): + return '\n'.join(lines).strip() + + +class CommentParser(object): + + def __init__(self, filename): + self.filename = filename + + def parse(self): + blocks = [] + current_block = [] + inside_single_line_block = False + inside_multi_line_block = False + + with open(self.filename) as fileobj: + for line in fileobj: + line = line.strip() + + if is_single_line_comment(line): + parsed = parse_single_line(line) + + if inside_single_line_block: + current_block.append(parsed) + else: + current_block = [parsed] + inside_single_line_block = True + + if is_multi_line_comment_start(line) or inside_multi_line_block: + parsed = parse_multi_line(line) + + if inside_multi_line_block: + current_block.append(parsed) + else: + current_block = [parsed] + inside_multi_line_block = True + + if is_multi_line_comment_end(line): + inside_multi_line_block = False + + if is_single_line_comment(line) is False and inside_multi_line_block is False: + if current_block: + blocks.append(normalize(current_block)) + + inside_single_line_block = False + current_block = [] + + return blocks + + @property + def blocks(self): + if not hasattr(self, '_blocks'): + self._blocks = self.parse() + return self._blocks diff --git a/pykss/modifier.py b/pykss/modifier.py new file mode 100644 index 0000000..0c31dd1 --- /dev/null +++ b/pykss/modifier.py @@ -0,0 +1,9 @@ +class Modifier(object): + + def __init__(self, name, description): + self.name = name + self.description = description + + @property + def class_name(self): + return self.name.replace('.', ' ').replace(':', ' pseudo-class-').strip() diff --git a/pykss/parser.py b/pykss/parser.py new file mode 100644 index 0000000..b306b62 --- /dev/null +++ b/pykss/parser.py @@ -0,0 +1,37 @@ +import os + +from pykss.comment import CommentParser +from pykss.section import Section + + +class Parser(object): + + def __init__(self, *paths): + self.paths = paths + + def parse(self): + sections = {} + + filenames = [os.path.join(subpath, filename) + for path in self.paths + for subpath, dris, files in os.walk(path) + for filename in files] + + for filename in filenames: + parser = CommentParser(filename) + for block in parser.blocks: + section = Section(block, os.path.basename(filename)) + section.parse() + if section.section: + sections[section.section] = section + + return sections + + @property + def sections(self): + if not hasattr(self, '_sections'): + self._sections = self.parse() + return self._sections + + def section(self, reference): + return self.sections.get(reference, Section()) diff --git a/pykss/section.py b/pykss/section.py new file mode 100644 index 0000000..bda7e7e --- /dev/null +++ b/pykss/section.py @@ -0,0 +1,57 @@ +import re + +from pykss.modifier import Modifier + + +CLASS_MODIFIER = '.' +PSEUDO_CLASS_MODIFIER = ':' +MODIFIER_DESCRIPTION_SEPARATOR = ' - ' +REFERENCE_START = 'Styleguide' + +reference_re = re.compile('%s ([\d\.]+)' % REFERENCE_START) + + +class Section(object): + + def __init__(self, comment=None, filename=None): + self.comment = comment + self.filename = filename + + def parse(self): + self._description_lines = [] + self._modifiers = [] + self._reference = None + + for line in self.comment.splitlines(): + if line.startswith(CLASS_MODIFIER) or line.startswith(PSEUDO_CLASS_MODIFIER): + try: + modifier, description = line.split(MODIFIER_DESCRIPTION_SEPARATOR) + except ValueError: + pass + else: + self._modifiers.append(Modifier(modifier.strip(), description.strip())) + + elif line.startswith(REFERENCE_START): + self._reference = reference_re.match(line).groups()[0].rstrip('.') + else: + self._description_lines.append(line) + + self._description = '\n'.join(self._description_lines).strip() + + @property + def description(self): + if not hasattr(self, '_description'): + self.parse() + return self._description + + @property + def modifiers(self): + if not hasattr(self, '_modifiers'): + self.parse() + return self._modifiers + + @property + def section(self): + if not hasattr(self, '_reference'): + self.parse() + return self._reference diff --git a/runtests.py b/runtests.py new file mode 100644 index 0000000..b415412 --- /dev/null +++ b/runtests.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import sys + + +def runtests(args=None): + import pytest + + if not args: + args = [] + + if not any(a for a in args[1:] if not a.startswith('-')): + args.append('tests') + + sys.exit(pytest.main(args)) + + +if __name__ == '__main__': + runtests(sys.argv) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..8d4c498 --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +from setuptools import setup, find_packages + + +setup( + name='pykss', + version='0.1', + description='Python implementation of KSS', + long_description=open('README.rst').read(), + author='Sean Brant', + author_email='brant.sean@gmail.com', + url='https://github.com/seanbrant/pykss', + license='BSD', + packages=find_packages(exclude=['tests']), + include_package_data=True, + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + ], + zip_safe=False, + test_suite='runtests.runtests', + extras_require={'tests': ['pytest', 'flake8']}, +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/comments.txt b/tests/fixtures/comments.txt new file mode 100644 index 0000000..37dcdf6 --- /dev/null +++ b/tests/fixtures/comments.txt @@ -0,0 +1,33 @@ +This file is used for generic comment parsing across CSS, SCSS, SASS & LESS. + +There's single-line comment styles: + +// This comment block has comment identifiers on every line. +// +// Fun fact: this is Kyle's favorite comment syntax! + + +There's block comment styles: + +/* This comment block is a block-style comment syntax. + +There's only two identifier across multiple lines. */ + +/* This is another common multi-line comment style. + * + * It has stars at the begining of every line. + */ + + +Some people do crazy things like mix comment styles: + +// This comment has a /* comment */ identifier inside of it! + +/* Look at my //cool// comment art! */ + + +Indented comments: + + // Indented single-line comment. + + /* Indented block comment. */ \ No newline at end of file diff --git a/tests/fixtures/css/buttons.css b/tests/fixtures/css/buttons.css new file mode 100644 index 0000000..575f934 --- /dev/null +++ b/tests/fixtures/css/buttons.css @@ -0,0 +1,38 @@ +/* +Your standard form button. + +:hover - Highlights when hovering. +:disabled - Dims the button when disabled. +.primary - Indicates button is the primary action. +.smaller - A little bit smaller now. + +Styleguide 2.1.1. +*/ +button { + padding: 5px 15px; } + button.primary, button.primary:hover { + color: #fff; } + button.smaller { + font-size: 11px; } + button:hover { + color: #337797; } + button:disabled { + opacity: 0.5; } + + +/* +A button suitable for giving stars to someone. + +.star-given - A highlight indicating you've already given a star. +.disabled - Dims the button to indicate it cannot be used. + +Styleguide 2.2.1. +*/ +a.button.star { + display: inline-block; } + a.button.star .star { + font-size: 10px; } + a.button.star.star-given { + color: #ae7e00; } + a.button.star.disabled { + opacity: 0.5; } \ No newline at end of file diff --git a/tests/fixtures/less/_label.less b/tests/fixtures/less/_label.less new file mode 100644 index 0000000..43c13a4 --- /dev/null +++ b/tests/fixtures/less/_label.less @@ -0,0 +1,10 @@ +/* +A default form label + +Styleguide 5.0.0 +*/ +label { + display: block; + float: left; + width: 150px; +} \ No newline at end of file diff --git a/tests/fixtures/less/buttons.less b/tests/fixtures/less/buttons.less new file mode 100644 index 0000000..afc58ac --- /dev/null +++ b/tests/fixtures/less/buttons.less @@ -0,0 +1,51 @@ +/* +Your standard form button. + +:hover - Highlights when hovering. +:disabled - Dims the button when disabled. +.primary - Indicates button is the primary action. +.smaller - A little bit smaller now. + +Styleguide 2.1.1. +*/ +button, .button { + padding:5px 15px; + + &.primary, &.primary:hover{ + color:#fff; + } + + &.smaller{ + font-size:9px; + } + + &:hover{ + color:#337797; + } + + &:disabled{ + opacity:0.5; + } +} + +/* +A button suitable for giving stars to someone. + +.star-given - A highlight indicating you've already given a star. +.disabled - Dims the button to indicate it cannot be used. + +Styleguide 2.2.1. +*/ +a.button.star{ + display:inline-block; + + .star{ font-size:10px; } + + &.star-given{ + color:#ae7e00; + } + + &.disabled{ + opacity:0.5; + } +} \ No newline at end of file diff --git a/tests/fixtures/less/mixins.less b/tests/fixtures/less/mixins.less new file mode 100644 index 0000000..87b5272 --- /dev/null +++ b/tests/fixtures/less/mixins.less @@ -0,0 +1,8 @@ +/* +Your standard grid helper. + +Styleguide 4.0.0. +*/ +.grid(@columns, @max: 10) { + width: (@columns / @max) * 960px; +} \ No newline at end of file diff --git a/tests/fixtures/less/nested.less b/tests/fixtures/less/nested.less new file mode 100644 index 0000000..3eea176 --- /dev/null +++ b/tests/fixtures/less/nested.less @@ -0,0 +1,17 @@ +@import "_label"; +/* +Your standard form element. + +Styleguide 3.0.0 +*/ +form { + + /* + Your standard text input box. + + Styleguide 3.0.1 + */ + input[type="text"] { + border: 1px solid #ccc; + } +} \ No newline at end of file diff --git a/tests/fixtures/sass/buttons.sass b/tests/fixtures/sass/buttons.sass new file mode 100644 index 0000000..2a8bcdf --- /dev/null +++ b/tests/fixtures/sass/buttons.sass @@ -0,0 +1,40 @@ +/* Your standard form button. + * + * :hover - Highlights when hovering. + * :disabled - Dims the button when disabled. + * .primary - Indicates button is the primary action. + * .smaller - A little bit smaller now. + * + * Styleguide 2.1.1. */ +button + padding: 5px 15px + + &.primary, &.primary:hover + color: #fff + + &.smaller + font-size: 11px + + &:hover + color: #337797 + + &:disabled + opacity: 0.5 + +// A button suitable for giving stars to someone. +// +// .star-given - A highlight indicating you've already given a star. +// .disabled - Dims the button to indicate it cannot be used. +// +// Styleguide 2.2.1. +a.button.star + display: inline-block + + .star + font-size: 10px + + &.star-given + color: #ae7e00 + + &.disabled + opacity: 0.5 \ No newline at end of file diff --git a/tests/fixtures/sass/nested.sass b/tests/fixtures/sass/nested.sass new file mode 100644 index 0000000..ae0309e --- /dev/null +++ b/tests/fixtures/sass/nested.sass @@ -0,0 +1,10 @@ +/* Your standard form element. + * + * Styleguide 3.0.0 */ +form + + /* Your standard text input box. + * + * Styleguide 3.0.1 */ + input[type="text"] + border: 1px solid #ccc \ No newline at end of file diff --git a/tests/fixtures/scss/buttons.scss b/tests/fixtures/scss/buttons.scss new file mode 100644 index 0000000..efa3bb1 --- /dev/null +++ b/tests/fixtures/scss/buttons.scss @@ -0,0 +1,47 @@ +// Your standard form button. +// +// :hover - Highlights when hovering. +// :disabled - Dims the button when disabled. +// .primary - Indicates button is the primary action. +// .smaller - A little bit smaller now. +// +// Styleguide 2.1.1. +button{ + padding:5px 15px; + + &.primary, &.primary:hover{ + color:#fff; + } + + &.smaller{ + font-size:11px; + } + + &:hover{ + color:#337797; + } + + &:disabled{ + opacity:0.5; + } +} + +// A button suitable for giving stars to someone. +// +// .star-given - A highlight indicating you've already given a star. +// .disabled - Dims the button to indicate it cannot be used. +// +// Styleguide 2.2.1. +a.button.star{ + display:inline-block; + + .star{ font-size:10px; } + + &.star-given{ + color:#ae7e00; + } + + &.disabled{ + opacity:0.5; + } +} \ No newline at end of file diff --git a/tests/fixtures/scss/nested.scss b/tests/fixtures/scss/nested.scss new file mode 100644 index 0000000..2e51c2b --- /dev/null +++ b/tests/fixtures/scss/nested.scss @@ -0,0 +1,12 @@ +// Your standard form element. +// +// Styleguide 3.0.0 +form { + + // Your standard text input box. + // + // Styleguide 3.0.1 + input[type="text"] { + border: 1px solid #ccc; + } +} \ No newline at end of file diff --git a/tests/test_comment.py b/tests/test_comment.py new file mode 100644 index 0000000..ea47156 --- /dev/null +++ b/tests/test_comment.py @@ -0,0 +1,65 @@ +import os +import unittest + +from pykss import comment + + +class CommentMethodTestCase(unittest.TestCase): + + def test_detects_single_line_comment_syntax(self): + self.assertTrue(comment.is_single_line_comment('// yuuuuup')) + self.assertFalse(comment.is_single_line_comment('nooooope')) + + def test_detects_start_of_multi_line_comment_syntax(self): + self.assertTrue(comment.is_multi_line_comment_start('/* yuuuuup')) + self.assertFalse(comment.is_multi_line_comment_start('nooooope')) + + def test_detects_end_of_multi_line_comment_syntax(self): + self.assertTrue(comment.is_multi_line_comment_end(" yuuuuup */")) + self.assertFalse(comment.is_multi_line_comment_end("nooooope")) + + def test_parses_the_single_line_comment_syntax(self): + self.assertEqual(comment.parse_single_line("// yuuuuup"), 'yuuuuup') + + def test_parses_the_multi_line_comment_syntax(self): + self.assertEqual(comment.parse_multi_line('/* yuuuup */'), 'yuuuup') + + +class CommentParserTestCase(unittest.TestCase): + + def setUp(self): + text = os.path.join(os.path.dirname(__file__), 'fixtures', 'comments.txt') + self.comments = comment.CommentParser(text).blocks + + def test_finds_single_line_comment_styles(self): + expected = """ +This comment block has comment identifiers on every line. + +Fun fact: this is Kyle's favorite comment syntax!""" + self.assertTrue(expected.strip() in self.comments) + + def test_finds_block_style_comment_styles(self): + expected = """ +This comment block is a block-style comment syntax. + +There's only two identifier across multiple lines. + """ + self.assertTrue(expected.strip() in self.comments) + + expected = """ +This is another common multi-line comment style. + +It has stars at the begining of every line. + """ + self.assertTrue(expected.strip() in self.comments) + + def test_handles_mixed_styles(self): + expected = "This comment has a /* comment */ identifier inside of it!" + self.assertTrue(expected.strip() in self.comments) + + expected = 'Look at my //cool// comment art!' + self.assertTrue(expected.strip() in self.comments) + + def test_handles_indented_comments(self): + self.assertTrue('Indented single-line comment.' in self.comments) + self.assertTrue('Indented block comment.' in self.comments) diff --git a/tests/test_modifier.py b/tests/test_modifier.py new file mode 100644 index 0000000..1e109d8 --- /dev/null +++ b/tests/test_modifier.py @@ -0,0 +1,15 @@ +import unittest + +from pykss.modifier import Modifier + + +class ModiferTestCase(unittest.TestCase): + + def setUp(self): + self.modifier = Modifier('.callout.extreme:hover', 'calls things out') + + def test_handles_pseudo(self): + self.assertTrue('pseudo-class-hover' in self.modifier.class_name) + + def test_handles_multiple_classes(self): + self.assertTrue('callout extreme' in self.modifier.class_name) diff --git a/tests/test_parser.py b/tests/test_parser.py new file mode 100644 index 0000000..1471160 --- /dev/null +++ b/tests/test_parser.py @@ -0,0 +1,48 @@ +import os +import unittest + +import pykss + + +class ParseTestCase(unittest.TestCase): + + def setUp(self): + fixtures = os.path.join(os.path.dirname(__file__), 'fixtures') + self.scss = pykss.Parser(os.path.join(fixtures, 'scss')) + self.less = pykss.Parser(os.path.join(fixtures, 'less')) + self.sass = pykss.Parser(os.path.join(fixtures, 'sass')) + self.css = pykss.Parser(os.path.join(fixtures, 'css')) + self.multiple = pykss.Parser(os.path.join(fixtures, 'scss'), os.path.join(fixtures, 'less')) + + def test_parses_kss_comments_in_scss(self): + self.assertEqual(self.scss.section('2.1.1').description, 'Your standard form button.') + + def test_parses_kss_comments_in_less(self): + self.assertEqual(self.less.section('2.1.1').description, 'Your standard form button.') + + def test_parses_kss_multi_line_comments_in_sass(self): + self.assertEqual(self.sass.section('2.1.1').description, 'Your standard form button.') + + def test_parses_kss_single_line_comments_in_sass(self): + self.assertEqual(self.sass.section('2.2.1').description, 'A button suitable for giving stars to someone.') + + def test_parses_kss_comments_in_css(self): + self.assertEqual(self.css.section('2.1.1').description, 'Your standard form button.') + + def test_parses_nested_scss_documents(self): + self.assertEqual(self.scss.section('3.0.0').description, 'Your standard form element.') + self.assertEqual(self.scss.section('3.0.1').description, 'Your standard text input box.') + + def test_parses_nested_less_documents(self): + self.assertEqual(self.less.section('3.0.0').description, 'Your standard form element.') + self.assertEqual(self.less.section('3.0.1').description, 'Your standard text input box.') + + def test_parses_nested_sass_documents(self): + self.assertEqual(self.sass.section('3.0.0').description, 'Your standard form element.') + self.assertEqual(self.sass.section('3.0.1').description, 'Your standard text input box.') + + def test_parse_returns_dictionary_of_sections(self): + self.assertEqual(len(self.css.sections), 2) + + def test_parse_multiple_paths(self): + self.assertEqual(len(self.multiple.sections), 6) diff --git a/tests/test_section.py b/tests/test_section.py new file mode 100644 index 0000000..7ba8496 --- /dev/null +++ b/tests/test_section.py @@ -0,0 +1,36 @@ +import unittest + +from pykss.section import Section + + +class SectionTestCase(unittest.TestCase): + + def setUp(self): + comment = """ +# Form Button + +Your standard form button. + +:hover - Highlights when hovering. +:disabled - Dims the button when disabled. +.primary - Indicates button is the primary action. +.smaller - A smaller button + +Styleguide 2.1.1. + """ + self.section = Section(comment.strip(), 'example.css') + + def test_parses_the_description(self): + self.assertEqual(self.section.description, '# Form Button\n\nYour standard form button.') + + def test_parses_the_modifiers(self): + self.assertEqual(len(self.section.modifiers), 4) + + def test_parses_modifier_names(self): + self.assertEqual(self.section.modifiers[0].name, ':hover') + + def test_parses_modifier_descriptions(self): + self.assertEqual(self.section.modifiers[0].description, 'Highlights when hovering.') + + def test_parses_the_styleguide_reference(self): + self.assertEqual(self.section.section, '2.1.1') diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..9a92c2c --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +# Tox (http://codespeak.net/~hpk/tox/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py26, py27 + +[testenv] +commands = python setup.py test +deps = pytest -- cgit v1.2.3