diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9422f39..34cf5eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+# 0.2.2 - 2022-02-27
+* support using different orientation
# 0.2.1 - 2022-02-27
* disable a group if all its skills are new or not specified
# 0.2.0 - 2022-02-27
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a5d7f10
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2012-2022 Scott Chacon and others
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..59f67de
--- /dev/null
+++ b/README.md
@@ -0,0 +1,207 @@
+# skillmap
+A tool for generating skill map/tree like diagram.
+
+# What is a skill map/tree?
+Skill tree is a term used in video games, and it can be used for describing roadmaps for software project development as well.
+
+This project borrows inspiration and ideas from two sources:
+1. https://hacks.mozilla.org/2018/10/webassemblys-post-mvp-future/
+2. https://github.com/nikomatsakis/skill-tree
+
+# Installation
+```
+pip install skillmap
+```
+After installation, a `skillmap` command is available.
+
+# Usage
+1. Create a toml format skill map descriptor file. You can find more details about this descriptor format [here](docs/skillmap_descriptor.md). For a minimal example, see [`docs/examples/hello_world.toml`](docs/examples/hello_world.toml)
+```
+[skillmap]
+name = "hello world"
+icon = "bicycle"
+
+[groups.learn_python]
+name = "learn python"
+icon = "rocket"
+ [groups.learn_python.skills.print]
+ name = "print statement"
+ icon = "printer"
+ [groups.learn_python.skills.string]
+ name = "string literal"
+ icon = "book"
+```
+
+2. Run `skillmap path/to/your/skillmap.toml`
+ 1. For example, `skillmap docs/examples/hello_world.toml`
+3. Copy the generated skill map diagram to your clipboard.
+4. Paste the diagram to a mermaid diagram editor, for example, [`https://mermaid-js.github.io/mermaid-live-editor`](https://mermaid-js.github.io/mermaid-live-editor).
+
+# Examples
+```mermaid
+flowchart TD
+url_shortener(fa:fa-hashtag
url shortener)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.webui[fa:fa-desktop web ui]
+groups.webui.skills.url_validator(fa:fa-globe
url validator)
+class groups.webui.skills.url_validator newSkill;
+groups.webui.skills.react-->groups.webui.skills.url_validator
+groups.webui.skills.react(fa:fa-list
react)
+class groups.webui.skills.react beingLearnedSkill;
+
+end
+class groups.webui normalSkillGroup;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.native_client[fa:fa-desktop native client]
+groups.native_client.skills.react_native(fa:fa-mobile
react native)
+class groups.native_client.skills.react_native newSkill;
+
+groups.native_client.skills.other_clients(fa:fa-lock
???)
+class groups.native_client.skills.other_clients unknownSkill;
+
+end
+class groups.native_client newSkillGroup;
+groups.webui-->groups.native_client
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.backend[fa:fa-server backend]
+groups.backend.skills.restapi(fa:fa-send
REST API)
+class groups.backend.skills.restapi learnedSkill;
+
+groups.backend.skills.database(fa:fa-database
database)
+class groups.backend.skills.database learnedSkill;
+
+end
+class groups.backend normalSkillGroup;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.partitioned_backend[fa:fa-copy partitioned backend]
+groups.partitioned_backend.skills.proxy(fa:fa-anchor
proxy)
+class groups.partitioned_backend.skills.proxy beingLearnedSkill;
+
+groups.partitioned_backend.skills.database(fa:fa-database
partitioned database)
+class groups.partitioned_backend.skills.database beingLearnedSkill;
+
+end
+class groups.partitioned_backend normalSkillGroup;
+groups.backend-->groups.partitioned_backend
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.cache[ cache]
+groups.cache.skills.memcache(fa:fa-magnet
memcache)
+class groups.cache.skills.memcache learnedSkill;
+
+groups.cache.skills.redis(fa:fa-lock
???)
+class groups.cache.skills.redis unknownSkill;
+
+end
+class groups.cache normalSkillGroup;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+url_shortener-->groups.webui
+url_shortener-->groups.backend
+url_shortener-->groups.cache
+classDef normalSkillGroup stroke:#0096C7,stroke-width:4px,fill:#CAF0F8;
+
+classDef beingLearnedSkill stroke-width:2px,stroke:#90E0EF,fill:#ADE8F4;
+classDef learnedSkill stroke-width:2px,stroke:#00B4D8,fill:#48CAE4;
+
+classDef newSkillGroup stroke-width:4px,stroke:#D6CCC2,fill:#EDEDE9;
+classDef newSkill stroke-width:2px,stroke:#D6CCC2,fill:#EDEDE9;
+classDef unknownSkill stroke-width:2px,stroke:#D6CCC2,fill:#EDEDE9;
+
+class url_shortener normalSkillGroup;
+```
+
+```mermaid
+flowchart TD
+url_shortener(fa:fa-hashtag
url shortener)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.webui[fa:fa-desktop web ui]
+groups.webui.skills.url_validator(fa:fa-globe
url validator)
+class groups.webui.skills.url_validator newSkill;
+groups.webui.skills.react-->groups.webui.skills.url_validator
+groups.webui.skills.react(fa:fa-list
react)
+class groups.webui.skills.react beingLearnedSkill;
+
+end
+class groups.webui normalSkillGroup;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.native_client[fa:fa-desktop native client]
+groups.native_client.skills.react_native(fa:fa-mobile
react native)
+class groups.native_client.skills.react_native newSkill;
+
+groups.native_client.skills.other_clients(fa:fa-lock
???)
+class groups.native_client.skills.other_clients unknownSkill;
+
+end
+class groups.native_client newSkillGroup;
+groups.webui-->groups.native_client
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.backend[fa:fa-server backend]
+groups.backend.skills.restapi(fa:fa-send
REST API)
+class groups.backend.skills.restapi learnedSkill;
+
+groups.backend.skills.database(fa:fa-database
database)
+class groups.backend.skills.database learnedSkill;
+
+end
+class groups.backend normalSkillGroup;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.partitioned_backend[fa:fa-copy partitioned backend]
+groups.partitioned_backend.skills.proxy(fa:fa-anchor
proxy)
+class groups.partitioned_backend.skills.proxy beingLearnedSkill;
+
+groups.partitioned_backend.skills.database(fa:fa-database
partitioned database)
+class groups.partitioned_backend.skills.database beingLearnedSkill;
+
+end
+class groups.partitioned_backend normalSkillGroup;
+groups.backend-->groups.partitioned_backend
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+subgraph groups.cache[ cache]
+groups.cache.skills.memcache(fa:fa-magnet
memcache)
+class groups.cache.skills.memcache learnedSkill;
+
+groups.cache.skills.redis(fa:fa-lock
???)
+class groups.cache.skills.redis unknownSkill;
+
+end
+class groups.cache normalSkillGroup;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+url_shortener-->groups.webui
+url_shortener-->groups.backend
+url_shortener-->groups.cache
+classDef normalSkillGroup stroke-width:4px,stroke:#DCEBCA,fill:#E9F5DB;
+classDef beingLearnedSkill stroke-width:2px,stroke:#C2D5AA,fill:#CFE1B9;
+classDef learnedSkill stroke-width:2px,stroke:#A6B98B,fill:#B5C99A;
+
+classDef newSkillGroup stroke-width:4px,stroke:#D6CCC2,fill:#EDEDE9;
+classDef newSkill stroke-width:2px,stroke:#D6CCC2,fill:#EDEDE9;
+classDef unknownSkill stroke-width:2px,stroke:#D6CCC2,fill:#EDEDE9;
+
+%% https://www.w3schools.com/colors/colors_groups.asp
+linkStyle default stroke-width:2px,stroke:OliveDrab;
+class url_shortener normalSkillGroup;
+```
+
+# License
+[MIT License](LICENSE)
+
+# More details
+* Skillmap toml descriptor format can be found [here](docs/skillmap_descriptor.md)
+* hot reloading when authoring a skillmap toml file
+ * install several tools to make hot reloading to work
+ * [`entr`](https://github.com/eradman/entr), run arbitrary commands when files change
+ * [Visual Studio Code](https://code.visualstudio.com) + [Markdown Preview Enhanced Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)
+ * Basically, use `entr` to watch toml file changes, and generate a `md` makrdown file using `skillmap` every time when toml file changes. And use `vscode` + `Markdown Preview Enhanced` extension to open this generated markdown file. Check out `build_sample` and `dev_sample` in [justfile](justfile) to see how to make hot reloading work
\ No newline at end of file
diff --git a/README.rst b/README.rst
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/examples/hello_world.toml b/docs/examples/hello_world.toml
new file mode 100644
index 0000000..9aebbdc
--- /dev/null
+++ b/docs/examples/hello_world.toml
@@ -0,0 +1,13 @@
+[skillmap]
+name = "hello world"
+icon = "bicycle"
+
+[groups.learn_python]
+name = "learn python"
+icon = "rocket"
+ [groups.learn_python.skills.print]
+ name = "print statement"
+ icon = "printer"
+ [groups.learn_python.skills.string]
+ name = "string literal"
+ icon = "book"
diff --git a/docs/skillmap_descriptor.md b/docs/skillmap_descriptor.md
new file mode 100644
index 0000000..ea716f3
--- /dev/null
+++ b/docs/skillmap_descriptor.md
@@ -0,0 +1,52 @@
+# Skillmap descriptor format
+* The skillmap descriptor format is a toml file. Here is a minimal example, see [`examples/hello_world.toml`](examples/hello_world.toml)
+* It describes three concepts in the file:
+ * `skillmap`: the root key of the toml file. Typically, it is used to represent a project.
+ * `group`: a group of skills. In each file, it can have multiple groups in it. You can use `group` to represent sub project/component in a project.
+ * `skill`: a specific skill. Each group can have multiple skills in it. You can use `skill` to represent a specific component/module in a sub project/component.
+### skillmap toml table
+* The `skillmap` toml table can have some fields:
+ * `name`: [optional] the name of the skillmap. It will be used as a label in the diagram for the top level node.
+ * `icon`: [optional] a fontawsome icon name. It will be used as an icon in the diagram. You can find the fontawsome icon list [here](https://fontawesome.com/v4.7.0/icons/).
+ * `theme`: [optional] theme for the diagram. Serveral themes are included:
+ * ocean (default theme)
+ * earth
+ * grape
+ * grass
+ * pale
+ * rose
+ * `orientation`: [optional] the orientation of the diagram. [All mermaid's orientations](https://mermaid-js.github.io/mermaid/#/flowchart?id=flowchart-orientation) are supported, including:
+ * TB - top to bottom (default)
+ * TD - top-down/ same as top to bottom
+ * BT - bottom to top
+ * RL - right to left
+ * LR - left to right
+## group/skill toml tables
+* The `group`/`skill` toml table can have some fields:
+ * `name`: [optional] the name of the skillmap/group/skill. It will be used as a label in the diagram. .
+ * `icon`: [optional] a fontawsome icon name. It will be used as an icon in the diagram. You can find the fontawsome icon list [here](https://fontawesome.com/v4.7.0/icons/).
+ * `requires`: [optional] a list of strings. It indicates a list of skill groups or skills to be learned before this learning this skill group/skill. where each string is a toml table name of a group/skill. It will be rendered as an edge(s) from one node to another.
+
+## Example
+```toml
+[groups.learn_python]
+name = "learn python"
+icon = "rocket"
+ [groups.learn_python.skills.print]
+ name = "print statement"
+ icon = "printer"
+ [groups.learn_python.skills.string]
+ name = "string literal"
+ icon = "book"
+
+[groups.program_with_python]
+name = "program with python"
+icon = "car"
+requires = ["groups.learn_python"]
+```
+In this exmaple, there are:
+* two groups: `groups.learn_python` and `groups.program_with_python`
+* `groups.learn_python` has two skills:
+ * `groups.learn_python.skills.print`
+ * `groups.learn_python.skills.string`
+* `groups.program_with_python` requires `groups.learn_python` to be learned first. When drawn in the diagram, it will be rendered as an edge from `groups.learn_python` to `groups.program_with_python`.
\ No newline at end of file
diff --git a/justfile b/justfile
index 2e5391f..69bd079 100644
--- a/justfile
+++ b/justfile
@@ -5,3 +5,9 @@ setup:
# install the library into system python
rm -fr ./dist
poetry build && pip install ./dist/skillmap-*-py3-none-any.whl --force-reinstall
+
+build_sample src="tests/url_shortener.toml" dest="dist/url_shortener.md":
+ echo '```mermaid' > {{ dest }} && skillmap {{ src }} >> {{ dest }} && echo '```' >> {{ dest }}
+
+dev_sample src="tests":
+ find {{ src }} -iname "*.toml" | entr -s "just build_sample"
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index b7c531b..ce7d424 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "skillmap"
-version = "0.1.0"
+version = "0.2.2"
description = "Skillmap generates a skill tree from a toml file"
authors = ["Yue Ni "]
diff --git a/skillmap/main.py b/skillmap/main.py
index ae6e7b2..f8222ad 100644
--- a/skillmap/main.py
+++ b/skillmap/main.py
@@ -20,10 +20,10 @@
def _skillmap_parser():
parser = argparse.ArgumentParser(prog="skillmap")
parser.add_argument(
- "skillmap",
+ "descriptor_toml",
default=False,
type=str,
- help="Skillmap descriptor toml file",
+ help="The path to a toml file describing the skillmap",
)
parser.add_argument(
"--version",
@@ -32,13 +32,13 @@ def _skillmap_parser():
help="show version number",
)
- parser.add_argument(
- "-f",
- "--format",
- type=str,
- default="mermaid",
- help="export format, [mermaid] are supported",
- )
+ # parser.add_argument(
+ # "-f",
+ # "--format",
+ # type=str,
+ # default="mermaid",
+ # help="export format, [mermaid] are supported",
+ # )
return parser
@@ -56,7 +56,7 @@ def generate(skillmap_file, format = None):
def main():
args = parse_sys_args(sys.argv[1:])
- skillmap_file = Path(args["skillmap"])
- format = args["format"]
+ skillmap_file = Path(args["descriptor_toml"])
+ # format = args["format"]
skillmap_graph = generate(skillmap_file, format)
print(skillmap_graph)
diff --git a/skillmap/skillmap_visitor.py b/skillmap/skillmap_visitor.py
index f0987a1..78b6780 100644
--- a/skillmap/skillmap_visitor.py
+++ b/skillmap/skillmap_visitor.py
@@ -1,8 +1,11 @@
-import os
-
+from skillmap.theme_loader import load_theme
+import re
SECTION_SEPARATOR = "%" * 30
+def alphanumerize(s):
+ return re.sub(r'\W+', "_", s)
+
def get_icon(dict_value):
icon = ""
@@ -11,14 +14,6 @@ def get_icon(dict_value):
return icon
-def theme_css_styles(theme):
- theme_file = f"skillmap/themes/{theme}.theme"
- if not os.path.exists(theme_file):
- theme_file = f"skillmap/themes/ocean.theme"
- with open(theme_file, "r") as f:
- return f.read()
-
-
def _qualify(group_id):
return f"groups.{group_id}"
@@ -27,9 +22,13 @@ def _qualified_skill_id(qualified_group_id, skill_id):
return f"{qualified_group_id}.skills.{skill_id}"
-def groups_edges(map_name, groups):
- group_ids = [_qualify(group_id) for group_id, _ in groups.items()]
- groups_edges = "\n".join([f"{map_name}-->{gid}" for gid in group_ids])
+def groups_edges(map_id, groups):
+ group_ids = [
+ _qualify(group_id)
+ for group_id, group_value in groups.items()
+ if "requires" not in group_value # all groups without requires
+ ]
+ groups_edges = "\n".join([f"{map_id}-->{gid}" for gid in group_ids])
return groups_edges
@@ -112,25 +111,34 @@ def group_subgraphs(groups):
]
return "\n\n".join(group_graphs)
+def get_orientation(skill_map_dict):
+ orientation = skill_map_dict.get("orientation", "TD")
+ if orientation not in ["TD", "TB", "BT", "RL", "LR"]:
+ orientation = "TD"
+ return orientation
+
def skill_map_graph(skill_map):
skill_map_dict = skill_map.get("skillmap", {})
map_name = skill_map_dict.get("name", "")
+ map_id = alphanumerize(map_name)
theme = skill_map_dict.get("theme", "ocean")
+ orientation = get_orientation(skill_map_dict)
map_icon = get_icon(skill_map_dict)
- map_to_group_edges = groups_edges(map_name, skill_map.get("groups", {}))
+ map_to_group_edges = groups_edges(map_id, skill_map.get("groups", {}))
map_group_subgraphs = group_subgraphs(skill_map.get("groups", {}))
- skill_map_node = f"{map_name}({map_icon}
{map_name})"
- skill_map_node_style = f"class {map_name} normalSkillGroup;"
+ skill_map_node = f"{map_id}({map_icon}
{map_name})"
+ skill_map_node_style = f"class {map_id} normalSkillGroup;"
+ skill_map_header = f"flowchart {orientation}"
sections = [
- "flowchart TD",
+ skill_map_header,
skill_map_node,
map_group_subgraphs,
SECTION_SEPARATOR,
map_to_group_edges,
- theme_css_styles(theme),
+ load_theme(theme),
skill_map_node_style,
]
return "\n".join(sections)
diff --git a/skillmap/theme_loader.py b/skillmap/theme_loader.py
new file mode 100644
index 0000000..217dd71
--- /dev/null
+++ b/skillmap/theme_loader.py
@@ -0,0 +1,12 @@
+import pkgutil
+import os
+
+def load_theme(theme):
+ for try_theme in [theme, "ocean"]:
+ theme_file = f"themes/{try_theme}.theme"
+ try:
+ theme_data = pkgutil.get_data(__name__, theme_file)
+ if theme_data:
+ return theme_data.decode("utf-8")
+ except FileNotFoundError:
+ pass
\ No newline at end of file
diff --git a/tests/skillmap_parser_test.py b/tests/skillmap_parser_test.py
index d27dcf2..c0a4975 100644
--- a/tests/skillmap_parser_test.py
+++ b/tests/skillmap_parser_test.py
@@ -3,9 +3,9 @@
def test_parse_toml():
parser = SkillMapParser()
- skill_map = parser.parse('tests/urlshortener.toml')
+ skill_map = parser.parse('tests/url_shortener.toml')
assert skill_map
- assert skill_map['skillmap']['name'] == "urlshortener"
+ assert skill_map['skillmap']['name'] == "url shortener"
assert skill_map['groups']['webui']['name'] == "web ui"
assert skill_map['groups']['webui']['skills']['url_validator']['name'] == "url validator"
assert skill_map['groups']['webui']['skills']['url_validator']['icon'] == "globe"
diff --git a/tests/skillmap_visitor_test.py b/tests/skillmap_visitor_test.py
index 2fc55b9..884e1bc 100644
--- a/tests/skillmap_visitor_test.py
+++ b/tests/skillmap_visitor_test.py
@@ -8,15 +8,69 @@
def test_generate():
- skillmap_file = "tests/urlshortener.toml"
+ skillmap_file = "tests/url_shortener.toml"
skillmap_graph = generate(skillmap_file)
assert skillmap_graph
assert "flowchart TD" in skillmap_graph
- assert "urlshortener" in skillmap_graph
- assert "urlshortener-->groups.webui" in skillmap_graph
+ assert "url_shortener" in skillmap_graph
+ assert "url_shortener-->groups.backend" in skillmap_graph
assert "class groups.webui" in skillmap_graph
+def test_skillmap_node_with_missing_theme():
+ visitor = SkillMapVisitor()
+ skillmap_graph = visitor.visit(
+ {
+ "skillmap": {
+ "name": "url shortener",
+ "icon": "anchor",
+ "theme": "not_found",
+ }
+ }
+ )
+ assert skillmap_graph
+ assert "flowchart TD" in skillmap_graph
+ assert "url shortener" in skillmap_graph
+
+def test_skillmap_node_with_orientation():
+ visitor = SkillMapVisitor()
+ skillmap_graph = visitor.visit(
+ {
+ "skillmap": {
+ "name": "url shortener",
+ "icon": "anchor",
+ "orientation": "LR",
+ }
+ }
+ )
+ assert skillmap_graph
+ assert "flowchart LR" in skillmap_graph
+ assert "url shortener" in skillmap_graph
+
+def test_skillmap_node_with_auto_required_groups():
+ visitor = SkillMapVisitor()
+ skillmap_graph = visitor.visit(
+ {
+ "skillmap": {
+ "name": "url shortener",
+ },
+ "groups": {
+ "g1": {
+ "name": "g1",
+ },
+ "g2": {
+ "name": "g2",
+ "requires": ["g1"],
+ },
+ },
+ }
+ )
+ assert skillmap_graph
+ assert "flowchart TD" in skillmap_graph
+ assert "url_shortener-->groups.g1" in skillmap_graph
+ assert "url_shortener-->groups.g2" not in skillmap_graph
+
+
def test_skill_node():
skill_graph = skill_node("s1", {"name": "url validator", "icon": "globe"})
sections = ["s1(fa:fa-globe
url validator)", "class s1 newSkill;", ""]
@@ -30,16 +84,21 @@ def test_skill_node_with_status():
sections = ["s1(fa:fa-globe
url validator)", "class s1 beingLearnedSkill;", ""]
assert skill_graph.split("\n") == sections
+
def test_locked_skill_node():
skill_graph = skill_node("s1", {})
sections = ["s1(fa:fa-lock
???)", "class s1 unknownSkill;", ""]
assert skill_graph.split("\n") == sections
+
def test_skill_node_with_requires():
- skill_graph = skill_node("s1", {"name": "url validator", "icon": "globe", "requires": ["s2"]})
+ skill_graph = skill_node(
+ "s1", {"name": "url validator", "icon": "globe", "requires": ["s2"]}
+ )
sections = ["s1(fa:fa-globe
url validator)", "class s1 newSkill;", "s2-->s1"]
assert skill_graph.split("\n") == sections
+
def test_visit_group_without_skill():
group_graph = group_subgraph(
"g1",
@@ -54,7 +113,7 @@ def test_visit_group_without_skill():
"", # skill list is skipped
"end",
"class groups.g1 newSkillGroup;",
- ""
+ "",
]
assert group_graph.split("\n") == sections
@@ -82,10 +141,11 @@ def test_visit_group():
"",
"end",
"class groups.g1 newSkillGroup;",
- ""
+ "",
]
assert group_graph.split("\n") == sections
+
def test_visit_group_with_requires():
group_graph = group_subgraph(
"g1",
diff --git a/tests/url_shortener.toml b/tests/url_shortener.toml
new file mode 100644
index 0000000..136d71e
--- /dev/null
+++ b/tests/url_shortener.toml
@@ -0,0 +1,58 @@
+[skillmap]
+name = "url shortener"
+icon = "hashtag"
+theme = "pale"
+
+[groups.webui]
+name = "web ui"
+icon = "desktop"
+ [groups.webui.skills.url_validator]
+ name = "url validator"
+ icon = "globe"
+ requires = ["groups.webui.skills.react"]
+ [groups.webui.skills.react]
+ name = "react"
+ icon = "list"
+ status = "beingLearned"
+
+[groups.native_client]
+name = "native client"
+icon = "desktop"
+requires = ["groups.webui"]
+ [groups.native_client.skills.react_native]
+ name = "react native"
+ icon = "mobile"
+ [groups.native_client.skills.other_clients]
+
+[groups.backend]
+name = "backend"
+icon = "server"
+ [groups.backend.skills.restapi]
+ name = "REST API"
+ icon = "send"
+ status = "learned"
+ [groups.backend.skills.database]
+ name = "database"
+ icon = "database"
+ status = "learned"
+
+[groups.partitioned_backend]
+name = "partitioned backend"
+icon = "copy"
+requires = ["groups.backend"]
+ [groups.partitioned_backend.skills.proxy]
+ name = "proxy"
+ icon = "anchor"
+ status = "beingLearned"
+ [groups.partitioned_backend.skills.database]
+ name = "partitioned database"
+ icon = "database"
+ status = "beingLearned"
+
+[groups.cache]
+name = "cache"
+ [groups.cache.skills.memcache]
+ name = "memcache"
+ icon = "magnet"
+ status = "learned"
+ [groups.cache.skills.redis]
\ No newline at end of file
diff --git a/tests/urlshortener.toml b/tests/urlshortener.toml
deleted file mode 100644
index 9f175ca..0000000
--- a/tests/urlshortener.toml
+++ /dev/null
@@ -1,30 +0,0 @@
-[skillmap]
-name = "urlshortener"
-
-[groups.webui]
-name = "web ui"
-icon = "anchor"
-requires = ["groups.backend.skills.restapi"]
- [groups.webui.skills.url_validator]
- name = "url validator"
- icon = "globe"
- requires = ["groups.webui.skills.react"]
- [groups.webui.skills.react]
- name = "react"
- icon = "list"
- status = "beingLearned"
-
-[groups.backend]
-name = "backend"
- [groups.backend.skills.restapi]
- name = "REST API"
- icon = "server"
- [groups.backend.skills.database]
- name = "database"
- icon = "database"
- status = "learned"
-
-[groups.cache]
-name = "cache"
- [groups.cache.skills.memcached]
- [groups.cache.skills.redis]
\ No newline at end of file