Skip to content

Commit

Permalink
test: module 4 complete
Browse files Browse the repository at this point in the history
  • Loading branch information
tjbell committed Aug 23, 2018
1 parent 2b9bef0 commit 15f58ec
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 53 deletions.
3 changes: 2 additions & 1 deletion jobs/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ def close_connection(exception):
@app.route('/')
@app.route('/jobs')
def jobs():
return render_template('index.html')
jobs = query_db('SELECT job.id, job.title, job.description, job.salary, employer.id as employer_id, employer.name as employer_name FROM job JOIN employer ON employer.id = job.employer_id')
return render_template('index.html', jobs=jobs)
22 changes: 22 additions & 0 deletions jobs/templates/_macros.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% macro show_job(job) %}
<div class="card adjust-height">
<header class="card-header">
<p class="card-header-title">
<a href="{{ url_for('job', job_id=job['id']) }}">{{ job['title'] }}</a>
</p>
</header>
<div class="card-content">
<div class="content">
<a href="{{ url_for('employer', employer_id=job['employer_id']) }}">{{ job['employer_name'] }}</a><br>
${{ job['salary'] }}<br>
{{ job['description'] }}
</div>
</div>
</div>
{% endmacro %}

{% macro show_jobs(jobs) %}
{% for job in jobs %}
{{ show_job(job) }}
{% endfor %}
{% endmacro %}
13 changes: 13 additions & 0 deletions jobs/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends "layout.html" %}

{% block content %}
<div class="columns">
<div class="column">
<h1>Jobs</h1>
</div>
<div class="column is-one-fifth">
</div>
</div>

{{ show_jobs(jobs) }}
{% endblock %}
19 changes: 19 additions & 0 deletions jobs/templates/layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% from '_macros.html' import show_job, show_jobs with context %}

<!DOCTYPE html>
<html>
<head>
<title>Job Board</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/bulma.min.css') }}">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
</head>
<body>
<div class="container top">
<div class="content">
{% block content %}
{% endblock %}
</div>
</div>
</body>
</html>
5 changes: 2 additions & 3 deletions tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ For the `href` use the mustache template markup `{{}}` and the flask `url_for()`

@pytest.mark.extend_base_template To use `layout.html` as the base template:

- Open `index.html`, above the `<h1>` use the template markup `{% %}` and the extends tag to inherit `layout.html`.
- Wrap the `<h1>` element in a `block` called `content`.
- Open `index.html`, above the `<h1>` use the template markup `{% %}` and the extends tag to inherit `layout.html`.

**Preview**

Expand Down Expand Up @@ -270,7 +269,7 @@ In `<p>` tag add the following:

## 4.10 - Index Template

@pytest.mark.index_template Copy the HTML structure of the `index.html` file from `templates.html`. Paste in the new block replacing the existing `content` block.
@pytest.mark.index_template Copy the HTML structure of the `index.html` file from `templates.html`. Replace the `<h1>` with the copied HTML structure.

## 4.11 - Display All Jobs

Expand Down
5 changes: 2 additions & 3 deletions tests/test_module1.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ def test_templates_folder_module1():
def test_index_template_module1():
assert template_exists('index'), 'The `index.html` template does not exist in the `templates` folder.'
assert template_find('index', 'h1', limit=1), "The `<h1>` in the `index.html` template does not contain the contents 'Jobs'."
assert template_find('index', 'h1', limit=1).text == 'Jobs', "The `<h1>` in the `index.html` template does not contain the contents 'Jobs'."
assert template_find('index', 'h1', limit=1)[0].text == 'Jobs', "The `<h1>` in the `index.html` template does not contain the contents 'Jobs'."

@pytest.mark.app_index_route_function
def test_app_index_route_function_module1():
assert 'app' in dir(app), 'Have you created an instance of the `Flask` class called `app`?'
assert 'jobs' in dir(app), 'Have you created the `jobs` function?'
with app.app.app_context():
assert 'render_template:index.html' in get_functions(app.jobs)
assert 'render_template:index.html' in get_functions(app.jobs)

@pytest.mark.app_route_decoractors
def test_app_route_decoractors_module1():
Expand Down
8 changes: 4 additions & 4 deletions tests/test_module2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from jobs import app
from .utils import *

values = template_values('layout', 'url_for')
calls = template_functions('layout', 'url_for')

@pytest.mark.layout_template
def test_layout_template_module2():
Expand All @@ -12,12 +12,12 @@ def test_layout_template_module2():
@pytest.mark.add_bulma_css_framework
def test_add_bulma_css_framework_module2():
assert template_exists('layout'), 'The `layout.html` template does not exist in the `templates` folder.'
assert 'static:filename:css/bulma.min.css' in values, 'Looks like `bulma.min.css` is not linked in `layout.html`.'
assert 'static:filename:css/bulma.min.css' in calls, 'Looks like `bulma.min.css` is not linked in `layout.html`.'

@pytest.mark.add_custom_css
def test_add_custom_css_module2():
assert template_exists('layout'), 'The `layout.html` template does not exist in the `templates` folder.'
assert 'static:filename:css/app.css' in values, 'Looks like `app.css` is not linked in `layout.html`.'
assert 'static:filename:css/app.css' in calls, 'Looks like `app.css` is not linked in `layout.html`.'

@pytest.mark.add_fontawesome
def test_add_fontawesome_module2():
Expand All @@ -26,7 +26,7 @@ def test_add_fontawesome_module2():
'href': 'https://use.fontawesome.com/releases/v5.2.0/css/all.css',
'rel': 'stylesheet'
}
assert template_doc('layout').find('link', attr), 'Looks like FontAwesome is not linked in `layout.html`.'
assert template_soup('layout').find('link', attr), 'Looks like FontAwesome is not linked in `layout.html`.'

@pytest.mark.extend_base_template
def test_extend_base_template_module2():
Expand Down
43 changes: 31 additions & 12 deletions tests/test_module4.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,70 @@
import sys

from jobs import app
from .utils import *

@pytest.mark.template_macros
def test_template_macros_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'

@pytest.mark.show_job_macro_definition
def test_show_job_macro_definition_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert 'show_job:job' in template_macros('_macros'), 'Have you created the `show_job` macro and added the correct parameter?'

@pytest.mark.show_job_macro_html
def test_show_job_macro_html_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
html = template_macro_soup('_macros', 'show_job')
p = html.select('.card .card-header .card-header-title')
div = html.select('.card-content .content')
assert len(p) == 1 and len(div) == 1, 'Has the `HTML` from `templates.html` been copied to the `show_job` macro?'

@pytest.mark.show_job_macro_header
def test_show_job_macro_header_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.'
assert 'job:title' in template_macro_variables('_macros', 'show_job'), 'Looks like the job title link does not have content.'

@pytest.mark.show_job_macro_body
def test_show_job_macro_body_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert 'employer:employer_id:job:employer_id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.'
assert 'job:employer_name' in template_macro_variables('_macros', 'show_job'), 'Are you showing the employer name?'
assert 'job:salary' in template_macro_variables('_macros', 'show_job'), 'Are you showing the job salary?'
assert 'job:description' in template_macro_variables('_macros', 'show_job'), 'Are you showing the job description?'

@pytest.mark.show_jobs_macro_definition
def test_show_jobs_macro_definition_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert 'show_jobs:jobs' in template_macros('_macros'), 'Have you created the `show_jobs` macro and added the correct parameter?'

@pytest.mark.show_jobs_macro_for_loop
def test_show_jobs_macro_for_loop_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert 'job:jobs' in show_jobs_for(), 'Does the `show_jobs` macro contain a `for` loop?'

@pytest.mark.show_jobs_macro_for_loop_body
def test_show_jobs_macro_for_loop_body_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert 'show_job:job' in show_jobs_for(), 'Does the `show_jobs` macro call `show_job`?'

@pytest.mark.import_macros
def test_import_macros_module4():
pass
assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.'
assert '_macros.html:show_job:show_jobs:True' == template_import('layout'), 'Have you imported `_macros.html` in `layouts.html`?'

@pytest.mark.index_template
def test_index_template_module4():
pass
el = template_data('index').select('.columns .column.is-one-fifth')
assert len(el) == 1, 'Has the `HTML` from `templates.html` been copied to the `index.html` template?'

@pytest.mark.display_all_jobs
def test_display_all_jobs_module4():
pass
assert 'show_jobs:jobs' in template_functions('index', 'show_jobs'), 'Have you call the `show_jobs` macro in the `index.html` file?'

@pytest.mark.app_jobs_route_jobs
def test_app_jobs_route_jobs_module4():
pass
query_db = 'query_db:SELECT job.id, job.title, job.description, job.salary, employer.id as employer_id, employer.name as employer_name FROM job JOIN employer ON employer.id = job.employer_id'
assert query_db in get_functions(app.jobs), '`query_db` has not been called or has the wrong parameters.'
assert 'render_template:index.html:jobs:jobs' in get_functions(app.jobs), 'Have you added `jobs` to the `render_template` call.'
106 changes: 76 additions & 30 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,20 @@ def get_functions(source):
functions = []

def visit_Call(node):
path = ''
args = ''
name = node.func.attr if isinstance(node.func, ast.Attribute) else node.func.id

path = node.func.attr if isinstance(node.func, ast.Attribute) else node.func.id
if len(node.args) != 0:
args = ':'.join([str(val) for arg in node.args for val in build_dict(arg).values()])
path += ':' + ':'.join([str(val) for arg in node.args for val in build_dict(arg).values()])

if len(node.keywords) != 0:
path += ':' + ':'.join([str(val) for keyword in node.keywords for val in build_dict(keyword).values()])

path = name if args == '' else name + ':' + args
functions.append(path)

node_iter = ast.NodeVisitor()
node_iter.visit_Call = visit_Call
node_iter.visit(ast.parse(inspect.getsource(source)))
return functions

# def get_assignments(source):
# assignments = []
# def visit_Assign(node):
# assignments.append(build_dict(node))

# node_iter = ast.NodeVisitor()
# node_iter.visit_Assign = visit_Assign
# node_iter.visit(ast.parse(inspect.getsource(source)))
# return assignments

# def pair_exists(d, key, value):
# return key in d and value == d[key]

def build_dict(node):
result = {}
if node.__class__.__name__ == 'Is' or node.__class__.__name__ == 'Eq':
Expand All @@ -86,13 +72,6 @@ def build_dict(node):
result[attr] = value
return flatten(result, sep='/')

# def source_dict(source):
# return build_dict(ast.parse(inspect.getsource(source)))

# def source_search(source, node_type, expr):
# expr = "body[0].body[?node_type == `{}`]{}".format(node_type, expr)
# return search(expr, source_dict(source))

def list_routes(app):
rules = []

Expand All @@ -106,12 +85,72 @@ def list_routes(app):
def template_values(name, function):
values = []

for node in parsed_content(name).find_all(nodes.Call):
if node.node.name == function:
values.append(node.args[0].value + ':' + node.kwargs[0].key + ':' + node.kwargs[0].value.value)
for call in parsed_content(name).find_all(nodes.Call):
if call.node.name == function:
values.append(call.args[0].value + ':' + call.kwargs[0].key + ':' + call.kwargs[0].value.value)

return values

def template_functions(name, function_name):
functions = []

for call in parsed_content(name).find_all(nodes.Call):
if call.node.name == function_name:
args_string = ''
if isinstance(call.node, nodes.Name) and isinstance(call.args[0], nodes.Name):
args_string += call.node.name + ':' + call.args[0].name
else:
args = getattr(call, 'args')[0]
if isinstance(args, nodes.Const):
args_string += args.value + ':'
kwargs = call.kwargs[0] if len(getattr(call, 'kwargs')) > 0 else getattr(call, 'kwargs')
if isinstance(kwargs, nodes.Keyword):
args_string += kwargs.key + ':'
if isinstance(kwargs.value, nodes.Const):
args_string += kwargs.value.value
else:
args_string += kwargs.value.node.name
if isinstance(kwargs.value.arg, nodes.Const):
args_string += ':' + kwargs.value.arg.value
print(args_string)
functions.append(args_string)

return functions

def show_jobs_for():
values = []
for node in parsed_content('_macros').find_all(nodes.For):
values.append(node.target.name + ':' + node.iter.name)

for call in parsed_content('_macros').find_all(nodes.Call):
if call.node.name == 'show_job' and call.args[0].name == 'job':
values.append('show_job:job')

return values

def template_macros(name):
macros = []
for macro in parsed_content(name).find_all(nodes.Macro):
macros.append(macro.name + ':' + macro.args[0].name)
return macros

def template_macro_soup(name, macro_name):
for macro in parsed_content(name).find_all(nodes.Macro):
if macro.name == macro_name:
html = ''
for template_data in macro.find_all(nodes.TemplateData):
html += template_data.data
return source_soup(html)

def template_data(name):
html = ''
for node in parsed_content(name).find_all(nodes.TemplateData):
html += node.data
return source_soup(html)

def template_macro_variables(name, macro_name):
return [item.node.name + ':' + item.arg.value for item in parsed_content(name).find_all(nodes.Getitem)]

def template_exists(name):
return os.path.isfile('jobs/templates/' + name + '.html')

Expand All @@ -121,7 +160,10 @@ def template_source(name):
except exceptions.TemplateNotFound:
return None

def template_doc(name):
def source_soup(source):
return BeautifulSoup(source, 'html.parser')

def template_soup(name):
return BeautifulSoup(template_source(name), 'html.parser')

def template_find(name, tag, limit=None):
Expand All @@ -132,3 +174,7 @@ def parsed_content(name):

def template_extends(name):
return list(meta.find_referenced_templates(parsed_content(name)))

def template_import(name):
for node in parsed_content(name).find_all(nodes.FromImport):
return node.template.value + ':' + ':'.join(node.names) + ':' + str(node.with_context)

0 comments on commit 15f58ec

Please sign in to comment.