From 2525dc0231db8cff23ece3fc63c2af871090aba1 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 11:04:12 -0600 Subject: [PATCH 01/20] docs: restructure tasks --- tasks.md | 289 +++++++++++++++++++++--------------------- tests/test_module4.py | 2 +- tests/test_module7.py | 5 +- 3 files changed, 152 insertions(+), 144 deletions(-) diff --git a/tasks.md b/tasks.md index c8c700e51..ca0828c68 100644 --- a/tasks.md +++ b/tasks.md @@ -1,12 +1,10 @@ -# Build a Job Board with Python & Flask +# Setup -## Setup - -### Create Virtual Environment +## Create Virtual Environment In a terminal run the following commands from the root folder of the forked project. -``` shell +``` python -m venv venv ``` @@ -14,162 +12,162 @@ Once that completes, also run this command from the same folder. Windows -``` shell +``` \venv\Scripts\activate.bat ``` macOS & Linux -``` shell +``` source venv/bin/activate ``` Now that you are working in the virtualenv, install the project dependencies with the following command. -``` shell +``` pip install -r requirements.txt ``` -### Verify Setup +## Verify Setup In order to verify that everything is setup correctly, run the following command, which should show you the failing tests. This is good! We’ll be fixing this test once we jump into the build step. -``` shell +``` pytest ``` Every time you want to check your work locally you can type that command, and it will report the status of every task in the project. -### Previewing Your Work +## Previewing Your Work You can preview your work by running `flask run` in the root of your fork. Then visit `http://localhost:5000` in your browser. -## Module 01 - Flask Setup +# Module 01 - Flask Setup -### 1.1 - Import Flask +## 1.1 - Import Flask @pytest.mark.app_import_flask In order to create a flask application, import the `Flask` class and the `render_template` function from `flask` at the top of the `jobs/app.py` file. -### 1.2 - Create a Flask Application +## 1.2 - Create a Flask Application @pytest.mark.app_create_flask_app Still in `app.py` create an instance of the `Flask` class called `app`. Pass the special variable `__name__` to the `Flask` class constructor. -### 1.3 - Templates Folder +## 1.3 - Templates Folder @pytest.mark.templates_folder Create a folder called `templates` in the `jobs` directory. -### 1.4 - Create Index Template +## 1.4 - Create Index Template @pytest.mark.index_template In the root of the `templates` folder, create a file called `index.html`. Add a single line to the file: - `

Jobs

` -### 1.5 - Index Route Function +## 1.5 - Index Route Function -@pytest.mark.app_index_route_function The homepage of our job board will display all of the jobs in our database. +@pytest.mark.app_index_route_function The homepage of our job board will display all of the jobs in our database. -For now let’s setup a basic route that displays our simplified `index.html` template. +For now let’s setup a basic route that displays our simplified `index.html` template. -- Create a basic route in `app.py` by creating a function called `jobs`. +- Create a basic route in `app.py` by creating a function called `jobs`. - In the body of the function return a call to the `render_template()` function, pass a parameter of `index.html`. -### 1.6 - Route Decorators +## 1.6 - Route Decorators @pytest.mark.app_route_decoractors Still in `app.py`: -- Attach a `route()` decorator with the URL of `/` to the `jobs` function. -- Attach an additional route decorator of `/jobs`. +- Attach a `route()` decorator with the URL of `/` to the `jobs` function. +- Attach an additional route decorator of `/jobs`. **Note: The `jobs` function can now be reached at `/` and `/jobs`** -### Preview Module 1 - +**Preview** + At this point you have a working application with a single route. Try it out: - Open a terminal at the root of the project -- Run the command `flask run`. -- Open a browser and navigate to the URL: `http://localhost:5000`. +- Run the command `flask run`. +- Open a browser and navigate to the URL: `http://localhost:5000`. **Note: Appending `/jobs` should display the same page.** -## Module 02 - Base Template and Styling +# Module 02 - Base Template and Styling -### 2.1 - Create a Layout Template +## 2.1 - Create a Layout Template -@pytest.mark.layout_template We want each template to have a consistent look and feel. We can create a base layout that all templates can extend. +@pytest.mark.layout_template We want each template to have a consistent look and feel. We can create a base layout that all templates can extend. Create a new file called `layout.html` in the root of the `templates` folder. Next, copy the basic structure of this file from the file called `templates.html`. -### 2.2 - Add the Bulma CSS Framework +## 2.2 - Add the Bulma CSS Framework -@pytest.mark.add_bulma_css_framework The app will be styled with the [Bulma CSS Framework](bulma.io) and icons will be provided by [FontAwesome](fontawesome.com). +@pytest.mark.add_bulma_css_framework The app will be styled with the [Bulma CSS Framework](bulma.io) and icons will be provided by [FontAwesome](fontawesome.com). Add a `` tag to the head of `layout.html`. Give it an attribute of `rel="stylesheet"`. For the `href` use the mustache template markup `{{}}` and the flask `url_for()` function to construct a link for the file `css/bulma.min.css` in `static` folder. **Hint: use the keyword argument `filename`**. -### 2.3 - Add Custom CSS +## 2.3 - Add Custom CSS -@pytest.mark.add_custom_css For the second `` tag in `layout.html` construct an `href` for the file `css/app.css`, also in the `static` folder, using the same method. Don't forget the `rel` attribute. +@pytest.mark.add_custom_css For the second `` tag in `layout.html` construct an `href` for the file `css/app.css`, also in the `static` folder, using the same method. Don't forget the `rel` attribute. -### 2.4 - Add FontAwesome +## 2.4 - Add FontAwesome -@pytest.mark.add_fontawesome The last `` tag in `layout.html` should have an `href` value of `https://use.fontawesome.com/releases/v5.2.0/css/all.css`. Make sure to preview the application and check out the _awesome_ styling. +@pytest.mark.add_fontawesome The last `` tag in `layout.html` should have an `href` value of `https://use.fontawesome.com/releases/v5.2.0/css/all.css`. Make sure to preview the application and check out the _awesome_ styling. -### 2.5 - Extend Base Template +## 2.5 - Extend Base Template @pytest.mark.extend_base_template To use `layout.html` as the base template: - Open `index.html`, above the `

` use the template markup `{% %}` and the extends tag to inherit `layout.html`. -### Preview Module 2 - -At this point you have a styled application. Check out the styles: +**Preview** +At this point you have a styled application. Check out the styles: + - Open a terminal at the root of the project -- Run the command `flask run`. +- Run the command `flask run`. - Open a browser and navigate to the URL: `http://localhost:5000`. -## Module 03 - Preparing for Dynamic Content +# Module 03 - Preparing for Dynamic Content -### 3.1 - Import SQLite +## 3.1 - Import SQLite @pytest.mark.app_import_sqlite At the top of `app.py` import the built_in `sqlite3` library. -### 3.2 - Import Global Namespace +## 3.2 - Import Global Namespace -@pytest.mark.app_import_g To provide access to the database throughout the application import the global helper `g` from `flask`. **Hint: the `from` statement already exists add `g` to the `import` comma separated list.** +@pytest.mark.app_import_g To provide access to the database throughout the application import the global helper `g` from `flask`. **Hint: the `from` statement already exists add `g` to the `import` comma separated list.** -### 3.3 - Database Path +## 3.3 - Database Path @pytest.mark.app_db_path Below all of the import statements, create a constant called `PATH`, that contains the path to the already created database stored in `db/jobs.sqlite`. -### 3.4 - Global Database Attribute +## 3.4 - Global Database Attribute -@pytest.mark.app_open_connection_get_attribute At the top of `app.py` create a function called `open_connection`. +@pytest.mark.app_open_connection_get_attribute At the top of `app.py` create a function called `open_connection`. -In the body of the `open_connection` function use the built_in `getattr()` function to get the `'_connection'` attribute from the `g` object, and set the default to `None`. Assign the return value of the `getattr` function to `db`. +In the body of the `open_connection` function use the built_in `getattr()` function to get the `'_connection'` attribute from the `g` object, and set the default to `None`. Assign the return value of the `getattr` function to `db`. -### 3.5 - Global Database Connection +## 3.5 - Global Database Connection @pytest.mark.app_open_connection_connection Still in the `open_connection` function, test if `connection` is `None` if it is, set `connection` and `g._connection` to `sqlite3.connect(PATH)` using multiple assignment. -### 3.6 - sqlite3 Row Factory +## 3.6 - sqlite3 Row Factory @pytest.mark.app_open_connection_row_factory To make accessing data easier, after the if statement in `open_connection`: - Set the row_factory of `connection` to `sqlite3.Row`. **Note: All rows returned from the database will be named tuples.** - Return the `connection` variable. -### 3.7 - Query Database Function +## 3.7 - Query Database Function -@pytest.mark.app_execute_sql Let’s create a function to make it easier to query the database. +@pytest.mark.app_execute_sql Let’s create a function to make it easier to query the database. Below the `open_connection` function in `app.py` create a function called `execute_sql`. In the body of `execute_sql` create a variable called `db`. Assign this variable the return value of a call to the newly created `open_connection` function. -### 3.8 - Query Database Function Parameters +## 3.8 - Query Database Function Parameters @pytest.mark.app_execute_sql_parameters Still working with the `execute_sql` function: @@ -178,11 +176,11 @@ In the body of `execute_sql` create a variable called `db`. Assign this variable - Set the default of `commit` to `False`. - Set the default of `single` to `False`. -### 3.9 - Query Database Function Execute +## 3.9 - Query Database Function Execute @pytest.mark.app_execute_sql_execute In the body of `execute_sql` call the `execute` function on `connection`, pass in the `sql` and `values` variables. Assign the return value to a variable called `cursor`. -### 3.10 - Query Database Function Commit +## 3.10 - Query Database Function Commit @pytest.mark.app_execute_sql_commit In the body of `execute_sql`: @@ -192,7 +190,7 @@ In the body of `execute_sql` create a variable called `db`. Assign this variable - Close the cursor. - Return `results` variable. -### 3.11 - Close the Connection +## 3.11 - Close the Connection @pytest.mark.app_close_connection In order to make sure the database connection is closed when the `app_context` is torn down: @@ -201,155 +199,156 @@ In the body of `execute_sql` create a variable called `db`. Assign this variable In the function body: -- Call `getattr` with three arguments `g`, `'_connection'`, and `None` -- Assign the return value to a `connection` variable. -- If `connection` is not `None` `close` the `connection`. +- Call `getattr` with three arguments `g`, `'_connection'`, and `None` +- Assign the return value to a `connection` variable. +- If `connection` is not `None` `close` the `connection`. -### 3.12 - Close the Connection Decorator +## 3.12 - Close the Connection Decorator @pytest.mark.app_close_connection_decorator To ensure the `close_connection` function is called when the `app_context` is destroyed decorate it with `@app.teardown_appcontext`. -## Module 04 - Display All Jobs +# Module 04 - Display All Jobs -### 4.1 - Template Macros +## 4.1 - Template Macros -@pytest.mark.template_macros In the template folder create a new file called `_macros.html`. +@pytest.mark.template_macros In the template folder create a new file called `_macros.html`. -### 4.2 - Show Job Macro Definition +## 4.2 - Show Job Macro Definition @pytest.mark.show_job_macro_definition In `_macros.html` create a template macro, using the `macro` tag, called `show_job`. `show_job` should take one parameter called `job`. Don't forgot to end the macro. -### 4.3 - Show Job Macro HTML +## 4.3 - Show Job Macro HTML -@pytest.mark.show_job_macro_html Locate the `template.html` file in the root of the project. Open it and find the code labeled ``. Copy the code to the body of the `show_job` macro in `_macros.html`. +@pytest.mark.show_job_macro_html Locate the `template.html` file in the root of the project. Open it and find the code labeled +``. Copy the code to the body of the `show_job` macro in `_macros.html`. -### 4.4 - Show Job Macro Header +## 4.4 - Show Job Macro Header @pytest.mark.show_job_macro_header Still in the body of the `show_job` macro in `_macros.html` find the `

` tag with a class of `card_header_title`. -- Add an `` tag with an `href` of `{{ url_for('job', job_id=job['id']) }}`. -- The content should be `{{ job['title'] }}`. +- Add an `` tag with an `href` of `{{ url_for('job', job_id=job['id']) }}`. +- The content should be `{{ job['title'] }}`. -### 4.5 - Show Job Macro Body +## 4.5 - Show Job Macro Body -@pytest.mark.show_job_macro_body Next find the `

` with a class of `content` in the `show_job` macro and add a `

` tag. +@pytest.mark.show_job_macro_body Next find the `

` with a class of `content` in the `show_job` macro and add a `

` tag. In `

` tag add the following: -- `` tag with an `href` of `{{ url_for('employer', employer_id=job['employer_id']) }}`. The content should be `{{ job['employer_name'] }}`. +- `` tag with an `href` of `{{ url_for('employer', employer_id=job['employer_id']) }}`. The content should be `{{ job['employer_name'] }}`. - Line break -- ${{ job['salary'] }} +- ${{ job['salary'] }} - Line break - {{ job['description'] }} -### 4.6 - Show Jobs Macro Definition +## 4.6 - Show Jobs Macro Definition @pytest.mark.show_jobs_macro_definition In `_macros.html` create a template macro using the `macro` tag call it `show_jobs`. `show_jobs` should take one parameter called `jobs`. Don't forgot to end the macro. -### 4.7 - Show Jobs Macro For Loop +## 4.7 - Show Jobs Macro For Loop @pytest.mark.show_jobs_macro_for_loop Still in `_macros.html` and in the body of the `show_jobs` macro add the following HTML: - Add a `

` with two classes `columns` and `is-multiline`. - In this `
` add a `for in` loop that loops through all jobs. **Note: Use the `{% %}` template syntax, don’t forget about ending the `for` loop.** -### 4.8 - Show Jobs Macro For Loop Body +## 4.8 - Show Jobs Macro For Loop Body @pytest.mark.show_jobs_macro_for_loop_body In the body of the `for` loop add a `
` with two classes `column` and `is_half`. - In this `column` `
` add a call to the `show_job` macro passing in an individual `job` from the `for` loop. -### 4.9 - Import Macros +## 4.9 - Import Macros -@pytest.mark.import_macros In `templates/layouts.html` import the `show_job`, and `show_jobs` macros using the following code: +@pytest.mark.import_macros In `templates/layout.html` import the `show_job`, and `show_jobs` macros using the following code: -``` python +``` {% from '_macros.html' import show_job, show_jobs with context %} ``` -**Notes: Because each template extends `layouts.html` all of them will have access to these two new macros.** +**Notes: Because each template extends `layout.html` all of them will have access to these two new macros.** -### 4.10 - Index Template +## 4.10 - Index Template @pytest.mark.index_template Copy the HTML structure of the `index.html` file from `templates.html`. Replace the `

` with the copied HTML structure. -### 4.11 - Display All Jobs +## 4.11 - Display All Jobs @pytest.mark.display_all_jobs In the `index.html` template above the `{% endblock %}` add a call to the `show_jobs` macro passing in the argument `jobs`. -### 4.12 - Gather All Jobs +## 4.12 - Gather All Jobs -@pytest.mark.app_jobs_route_jobs In `app.py` locate the `jobs` function. +@pytest.mark.app_jobs_route_jobs In `app.py` locate the `jobs` function. Above the `render_template` function, call the `execute_sql` function: -- Pass in the SQL statement: `'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'`. -- Assign the results of the call to a variable called `jobs`. +- Pass in the SQL statement: `'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'`. +- Assign the results of the call to a variable called `jobs`. - In the `render_template` function, pass a keyword argument of `jobs=jobs`. -### Preview Module 4 +**Preview** At this point you can see all jobs on the homepage: - Open a terminal at the root of the project -- Run the command `flask run`. -- Open a browser and navigate to the URL: `http://localhost:5000`. +- Run the command `flask run`. +- Open a browser and navigate to the URL: `http://localhost:5000`. **Note: Appending `/jobs` should display the same page.** -## Module 05 - Display Individual Jobs +# Module 05 - Display Individual Jobs -### 5.1 - Job Template +## 5.1 - Job Template -@pytest.mark.app_job_template We need a template to display an individual job. Create a new file called `job.html` in the `template` folder. +@pytest.mark.app_job_template We need a template to display an individual job. Create a new file called `job.html` in the `template` folder. -In the file use an `extends` template tag to inherit `layout.html`. +In the file use an `extends` template tag to inherit `layout.html`. After the `extends` tag add a template `block` called `content`. In the block call the `show_job` macro passing in `job`. **Note: Use the `{{}}` for the macro call.** -### 5.2 - Job Route Function +## 5.2 - Job Route Function @pytest.mark.app_job_route In `app.py` create a function called `job`. In the body return a call to the `render_template` function passing in the newly created `job.html` template. -### 5.3 - Job Route Decorator +## 5.3 - Job Route Decorator -@pytest.mark.app_job_route_decorator We only need one job from the database, we will use the `execute_sql` function passing in a query with a where clause. In the where clause we will need a `job_id`. We are going to get this from the URL. +@pytest.mark.app_job_route_decorator We only need one job from the database, we will use the `execute_sql` function passing in a query with a where clause. In the where clause we will need a `job_id`. We are going to get this from the URL. -Still in `app.py`, add a route decorator with the URL path `/job/` to the `job` function. +Still in `app.py`, add a route decorator with the URL path `/job/` to the `job` function. -### 5.4 - Job Route Parameter +## 5.4 - Job Route Parameter -@pytest.mark.app_job_route_parameter To use the `job_id`, received from the URL, we need to pass it to the `job` function. Add `job_id` to the parameter list of the `job` function. +@pytest.mark.app_job_route_parameter To use the `job_id`, received from the URL, we need to pass it to the `job` function. Add `job_id` to the parameter list of the `job` function. -### 5.5 - Job Route Data +## 5.5 - Job Route Data -@pytest.mark.app_job_route_data In the `job` function, above the `render_template` function, call the `execute_sql` function and assign the results of the call to a `job` variable. +@pytest.mark.app_job_route_data In the `job` function, above the `render_template` function, call the `execute_sql` function and assign the results of the call to a `job` variable. Pass these three arguments to `execute_sql`: - SQL Query: `'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 WHERE job.id = ?'` - List Literal: [job_id] - single=True, This will bring back only one result. -### 5.6 - Job Route Pass Data +## 5.6 - Job Route Pass Data @pytest.mark.app_job_route_pass_data The template needs access to the job data. Let's pass the newly created variable `job` to the `render_template` function. This is done using the keyword argument syntax `job=job`. -### Preview Module 5 - +**Preview** + At this point you can see an individual job: - Open a terminal at the root of the project -- Run the command `flask run`. -- Open a browser and navigate to the URL: `http://localhost:5000/job/1`. +- Run the command `flask run`. +- Open a browser and navigate to the URL: `http://localhost:5000/job/1`. -## Module 06 - Display Individual Employers +# Module 06 - Display Individual Employers -### 6.1 - Employer Template +## 6.1 - Employer Template -@pytest.mark.employer_template To display an employer create a new file called `employer.html` in the `templates` folder. Open `templates.html`, find the appropriate block of HTML and copy and paste it to `employer.html`. +@pytest.mark.employer_template To display an employer create a new file called `employer.html` in the `templates` folder. Open `templates.html`, find the appropriate block of HTML and copy and paste it to `employer.html`. To the top of the file inherit from the `layout.html` template by using an `extends` template tag. -### 6.2 - Employer Template Details +## 6.2 - Employer Template Details @pytest.mark.employer_template_details Still in `employer.html` as the first thing in the template block add the following HTML: @@ -358,25 +357,25 @@ To the top of the file inherit from the `layout.html` template by using an `exte - Nested in the `
` add a `
` with a class of `description` - Nested in the description `
` add a`

` with the content {{ employer['description'] }} -### 6.3 - Employer Template All Jobs +## 6.3 - Employer Template All Jobs @pytest.mark.employer_template_all_jobs Below the `

` Jobs header in `employer.html` add a call to the `show_jobs` macro passing in `jobs`. -### 6.4 - Employer Template Reviews +## 6.4 - Employer Template Reviews -@pytest.mark.employer_template_reviews Still in `employer.html` find the review `

`, remove the comment surrounding the empty `{% %}` template tag. To this tag add a `for in` loop to loop through all `reviews`. Add the `endfor` directive to the second empty `{% %}` template tag, don't forget to the remove the comment. +@pytest.mark.employer_template_reviews Still in `employer.html` find the review `

`, remove the comment surrounding the empty `{% %}` template tag. To this tag add a `for in` loop to loop through all `reviews`. Add the `endfor` directive to the second empty `{% %}` template tag, don't forget to the remove the comment. -### 6.5 - Employer Template Review Stars +## 6.5 - Employer Template Review Stars @pytest.mark.employer_template_review_stars Still `employer.html` in the `
` with a class of `media_left` add this for loop: -``` python +``` {% for _ in range(1, review['rating']): %} {% endfor %} ``` -### 6.6 - Employer Template Review Details +## 6.6 - Employer Template Review Details @pytest.mark.employer_template_review_details Still in `employer.html` in the `content
` add a paragraph tag. In the paragraph display the details of a review: @@ -385,17 +384,17 @@ To the top of the file inherit from the `layout.html` template by using an `exte - `date` (Recommend Style: ``) - `review` -### 6.7 - Employer Route +## 6.7 - Employer Route -@pytest.mark.app_employer_route The template we have just built needs access to employer, job, and review data. Let's create a new function in `app.py` called `employer`. +@pytest.mark.app_employer_route The template we have just built needs access to employer, job, and review data. Let's create a new function in `app.py` called `employer`. Add a route decorator with a URL pattern of `/employer/`. In the body return a call to the `render_template` function passing in the `employer.html` template. -### 6.8 - Employer Route Employer Details +## 6.8 - Employer Route Employer Details -@pytest.mark.app_employer_route_employers Still working with the `employer` function add `employer_id` to the parameter list so that we have access to this value. Above the `render_template` function make a call to `execute_sql` and assign the return value to `employer`. +@pytest.mark.app_employer_route_employers Still working with the `employer` function add `employer_id` to the parameter list so that we have access to this value. Above the `render_template` function make a call to `execute_sql` and assign the return value to `employer`. Pass the following arguments to `execute_sql`: @@ -405,16 +404,16 @@ Pass the following arguments to `execute_sql`: In the `render_template` function, pass a keyword argument of `employer=employer`. -### 6.9 - Employer Route Employer Jobs +## 6.9 - Employer Route Employer Jobs -@pytest.mark.app_employer_route_jobs On the employer details page, we want to display all of the employers' jobs. In the `employer` function in `app.py` below the `employer` variable, add a call to the `execute_sql` function and assign the results to a variable called `jobs`. Pass the function two arguments: +@pytest.mark.app_employer_route_jobs On the employer details page, we want to display all of the employers' jobs. In the `employer` function in `app.py` below the `employer` variable, add a call to the `execute_sql` function and assign the results to a variable called `jobs`. Pass the function two arguments: - SQL Query: `'SELECT job.id, job.title, job.description, job.salary FROM job JOIN employer ON employer.id = job.employer_id WHERE employer.id = ?'` - List Literal: [employer_id] In the `render_template` function, add another keyword argument of `jobs=jobs` -### 6.10 - Employer Route Employer Review +## 6.10 - Employer Route Employer Review @pytest.mark.app_employer_route_reviews Still in the `employer` function in `app.py` below the jobs query add a new query to get all review for the employer. Make a call to `execute_sql` and assign the return value to `reviews`. Pass in the arguments: @@ -423,17 +422,17 @@ In the `render_template` function, add another keyword argument of `jobs=jobs` In the `render_template` function, add another keyword argument of `reviews=reviews` -### Preview - +**Preview** + At this point you can see an individual employer: - Open a terminal at the root of the project -- Run the command `flask run`. -- Open a browser and navigate to the URL: `http://localhost:5000/employer/1`. +- Run the command `flask run`. +- Open a browser and navigate to the URL: `http://localhost:5000/employer/1`. -## Module 07 - Employer Reviews +# Module 07 - Employer Reviews -### 7.1 - Review Template +## 7.1 - Review Template @pytest.mark.review_template To display a review form, create a new file called `review.html` in the templates folder. Open `templates.html`, find the appropriate block of HTML and copy and paste it to `review.html`. @@ -441,30 +440,36 @@ Inherit from the `layout.html` template by using an `extends` template tag. Find the cancel anchor tag. Add an `href` attribute with a value of `{{ url_for('employer', employer_id=employer_id) }}`. -### 7.2 - Review Route +## 7.2 - Review Route -@pytest.mark.app_review_route In `app.py` below the `employer` function create a new function called `review`. Add `employer_id` to the parameter list. +@pytest.mark.app_review_route In `app.py` below the `employer` function create a new function called `review`. Add `employer_id` to the parameter list. Add a route decorator with a URL pattern of `/employer//review`. Also add a keyword argument `methods` set to a tuple with two values: `'GET'` and `'POST'`. In the body of the function return the `render_template` function passing in the `review.html` template and a keyword argument of `employer_id=employer_id`. -### 7.3 - POST Request Check +## 7.3 - POST Request Check -@pytest.mark.app_review_post_request_check In the body of the `review` above the `render_template` function call, create an `if` statement that checks if `request.method` is equal to `'POST'`. +@pytest.mark.app_review_post_request_check In the body of the `review` above the `render_template` function call, create an `if` statement that checks if `request.method` is equal to `'POST'`. -- In the `if` statement create four variables `review`, `rating`, `title`, and `status`. Set them equal to their respective `request.form` values i.e. `request.form['review']`. +- In the `if` statement create four variables `review`, `rating`, `title`, and `status`. Set them equal to their respective `request.form` values i.e. `request.form['review']`. - Create a `date` variable assign it todays date formatted like '08/10/2018'. **Hint: Use `now()` and `strftime("%m/%d/%Y")`. If you use `now()` add an `import datetime` statement to the top of `app.py`.** -### 7.4 - Insert Review +## 7.4 - Insert Review + +@pytest.mark.app_review_insert_review Still in the `review` function below the variables in the `if` statement, call the `execute_sql` function with the following arguments: + +- 'INSERT INTO review (review, rating, title, date, status, employer_id) VALUES (?, ?, ?, ?, ?, ?)' +- (review, rating, title, date, status, employer_id) +- commit=True. -@pytest.mark.app_review_insert_review Still in the `review` function below the variables in the `if` statement, connect to the database, insert the form values, and commit the changes. Follow these steps: +## 7.5 - Redirect to Employer Page -- Assign a `db` variable to a call to `open_connection()` -- `execute` the following SQL statement on `db`: `'INSERT INTO review (review, rating, title, date, status, employer_id) VALUES (?, ?, ?, ?, ?, ?)'` passing the values: `(review, rating, title, date, status, employer_id)` -- `commit` the changes to the database. -- Return a redirect taking the user back to the employer page. **Hint: use `redirect()` and `url_for()` (pass a keyword argument of `employer_id=employer_id`) both of which need to be imported from flask.** +@pytest.mark.app_redirect_to_employer At the end of the function return the user back to the employer page. +This can be done by using the `redirect` and `url_for` functions. +To start import both of these functions from `flask`. Next return a call to `redirect` and pass in a call to the `url_for` function. +Pass the `url_for` function the route to redirect to which is the `employer` route and a keyword argument of `employer_id=employer_id`. -### 7.5 - Employer Review Button +## 7.6 - Employer Review Button @pytest.mark.employer_review_button Open the `employer.html` template and find the anchor tag to create a review. Add an `href` attribute with a value of `{{ url_for('review', employer_id=employer['id']) }}`. diff --git a/tests/test_module4.py b/tests/test_module4.py index 503d7d5b4..5de761f21 100644 --- a/tests/test_module4.py +++ b/tests/test_module4.py @@ -59,7 +59,7 @@ def test_show_jobs_macro_for_loop_body_module4(): @pytest.mark.test_import_macros_module4 def test_import_macros_module4(): 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`?' + assert '_macros.html:show_job:show_jobs:True' == template_import('layout'), 'Have you imported `_macros.html` in `layout.html`?' @pytest.mark.test_index_template_module4 def test_index_template_module4(): diff --git a/tests/test_module7.py b/tests/test_module7.py index 2fff3845a..64ae5b491 100644 --- a/tests/test_module7.py +++ b/tests/test_module7.py @@ -74,9 +74,12 @@ def test_app_review_post_request_check_module7(): @pytest.mark.test_app_review_insert_review_module7 def test_app_review_insert_review_module7(): assert 'review' in dir(app), 'Have you created the `review` function?' - execute_sql = "execute_sql:INSERT INTO review (review, rating, title, date, status, employer_id) VALUES (?, ?, ?, ?, ?, ?):[{'id': 'review'}, {'id': 'rating'}, {'id': 'title'}, {'id': 'date'}, {'id': 'status'}, {'id': 'employer_id'}]:commit:True" assert execute_sql in get_functions(app.review), '`execute_sql` has not been called or has the wrong parameters.' + +@pytest.mark.test_app_redirect_to_employer_module7 +def test_app_redirect_to_employer_module7(): + assert 'review' in dir(app), 'Have you created the `review` function?' assert 'redirect' in dir(app), '`redirect` has not been imported from flask.' assert 'url_for' in dir(app), '`url_for` has not been imported from flask.' assert 'redirect:employer:url_for:employer_id:employer_id' in get_functions(app.review), 'In the `if` are you redirect back to the employer page?' From 4dc65ab1bc8f968f3d5f3349a20d2d9b5ca7df45 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 12:59:46 -0600 Subject: [PATCH 02/20] test: module3 index error fix --- tests/test_module3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_module3.py b/tests/test_module3.py index d64986d31..d0c7b089c 100644 --- a/tests/test_module3.py +++ b/tests/test_module3.py @@ -53,6 +53,7 @@ def test_app_execute_sql_module3(): def test_app_execute_sql_parameters_module3(): assert 'execute_sql' in dir(app), 'Have you defined a function named `execute_sql`.' parameters = inspect.getfullargspec(app.execute_sql) + assert len(parameters.args) == 4, 'Have you added parameters to the `execute_sql` function.' assert parameters.args[0] == 'sql' and parameters.args[1] == 'values' and parameters.args[2] == 'commit' and parameters.args[3] == 'single', 'Have you added the correct parameters to the `execute_sql` function parameters list?' assert parameters.defaults[0] == () and parameters.defaults[1] == False and parameters.defaults[2] == False, 'Do the `args` and `one` parameters have the correct defaults in the `execute_sql` function parameters list?' From 42d0fb1dce6ea433dadf63286df440fb1938cf4b Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 13:22:27 -0600 Subject: [PATCH 03/20] test: module3 fix --- tests/test_module3.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_module3.py b/tests/test_module3.py index d0c7b089c..120fb774c 100644 --- a/tests/test_module3.py +++ b/tests/test_module3.py @@ -82,5 +82,7 @@ def test_app_close_connection_module3(): @pytest.mark.test_app_close_connection_decorator_module3 def test_app_close_connection_decorator_module3(): assert 'close_connection' in dir(app), 'Have you defined a function named `close_connection`.' - decorator = get_decorators(app.close_connection)['close_connection'][0][0] + decorators = get_decorators(app.close_connection)['close_connection'] + assert len(decorators) == 1, 'Have you added the correct decorator to `close_connection`.' + decorator = decorators[0][0] assert decorator == 'teardown_appcontext', 'Does `close_connection` have a `teardown_appcontext` decorator?' \ No newline at end of file From 491fa8c9dff2d5120f9ae7f9814bcbaf76319130 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 14:19:44 -0600 Subject: [PATCH 04/20] test: module4 fix --- tests/test_module4.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_module4.py b/tests/test_module4.py index 5de761f21..0999e9cf5 100644 --- a/tests/test_module4.py +++ b/tests/test_module4.py @@ -43,6 +43,7 @@ def test_show_jobs_macro_definition_module4(): @pytest.mark.test_show_jobs_macro_for_loop_module4 def test_show_jobs_macro_for_loop_module4(): 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?' html = template_macro_soup('_macros', 'show_jobs') div = html.select('div.columns.is-multiline') assert len(div) == 1, 'Has a `
` with classes of `columns` and `is-multiline` been added to the `show_jobs` macro?' @@ -51,6 +52,7 @@ def test_show_jobs_macro_for_loop_module4(): @pytest.mark.test_show_jobs_macro_for_loop_body_module4 def test_show_jobs_macro_for_loop_body_module4(): 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?' html = template_macro_soup('_macros', 'show_jobs') div = html.select('div.column.is-half') assert len(div) == 1, 'Has a `
` with classes of `column` and `is-half` been added to the `show_jobs` macro `for` loop body?' From ea0ef7d64cdc3dcfb6d419d39cbb9039fdbb6203 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 15:42:45 -0600 Subject: [PATCH 05/20] test: module fixes --- tests/test_module4.py | 2 -- tests/test_module5.py | 1 + tests/test_module6.py | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_module4.py b/tests/test_module4.py index 0999e9cf5..4df565230 100644 --- a/tests/test_module4.py +++ b/tests/test_module4.py @@ -24,13 +24,11 @@ def test_show_job_macro_html_module4(): @pytest.mark.test_show_job_macro_header_module4 def test_show_job_macro_header_module4(): 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_variables('_macros'), 'Looks like the job title link does not have content.' @pytest.mark.test_show_job_macro_body_module4 def test_show_job_macro_body_module4(): 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_variables('_macros'), 'Are you showing the employer name?' assert 'job:salary' in template_variables('_macros'), 'Are you showing the job salary?' assert 'job:description' in template_variables('_macros'), 'Are you showing the job description?' diff --git a/tests/test_module5.py b/tests/test_module5.py index 945c033fc..c7f34cff9 100644 --- a/tests/test_module5.py +++ b/tests/test_module5.py @@ -25,6 +25,7 @@ def test_app_job_route_decorator_module5(): @pytest.mark.test_app_job_route_parameter_module5 def test_app_job_route_parameter_module5(): assert 'job' in dir(app), 'Have you created the `job` function?' + assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.' assert 'job_id' in inspect.getfullargspec(app.job).args, 'Have you added the correct parameters to the `job` function parameters list?' @pytest.mark.test_app_job_route_data_module5 diff --git a/tests/test_module6.py b/tests/test_module6.py index b680f1fdb..74639e619 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -48,6 +48,7 @@ def test_app_employer_route_module6(): assert 'route:/employer/' in get_functions(app.employer) result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html')] assert len(result) == 1, 'Have you called the `render_template` function.' + assert 'employer:employer_id:job:employer_id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.' @pytest.mark.test_app_employer_route_employers_module6 def test_app_employer_route_employers_module6(): From 578b474cfa4f09c2b9af9c0b1e12d8284d7f9be5 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 15:49:25 -0600 Subject: [PATCH 06/20] test: module fixes --- tests/test_module5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_module5.py b/tests/test_module5.py index c7f34cff9..dba0a7475 100644 --- a/tests/test_module5.py +++ b/tests/test_module5.py @@ -20,7 +20,7 @@ def test_app_job_route_module5(): @pytest.mark.test_app_job_route_decorator_module5 def test_app_job_route_decorator_module5(): assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'route:/job/' in get_functions(app.job) + assert 'route:/job/' in get_functions(app.job), 'Have you added a `job_id` parameter. to the job function' @pytest.mark.test_app_job_route_parameter_module5 def test_app_job_route_parameter_module5(): From b0ad5287fac37a29879dd3d651902e676df8eb17 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 11 Sep 2018 15:50:01 -0600 Subject: [PATCH 07/20] test: module fixes --- tests/test_module5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_module5.py b/tests/test_module5.py index dba0a7475..c7a512646 100644 --- a/tests/test_module5.py +++ b/tests/test_module5.py @@ -20,7 +20,7 @@ def test_app_job_route_module5(): @pytest.mark.test_app_job_route_decorator_module5 def test_app_job_route_decorator_module5(): assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'route:/job/' in get_functions(app.job), 'Have you added a `job_id` parameter. to the job function' + assert 'route:/job/' in get_functions(app.job), 'Have you added a `job_id` parameter to the job function' @pytest.mark.test_app_job_route_parameter_module5 def test_app_job_route_parameter_module5(): From 51923d69268398d734d33fcf0a5a75d840ca56c5 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Wed, 12 Sep 2018 12:30:01 -0600 Subject: [PATCH 08/20] test: assert message fixes --- tests/test_module5.py | 2 +- tests/test_module7.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_module5.py b/tests/test_module5.py index c7a512646..9931019c2 100644 --- a/tests/test_module5.py +++ b/tests/test_module5.py @@ -25,7 +25,7 @@ def test_app_job_route_decorator_module5(): @pytest.mark.test_app_job_route_parameter_module5 def test_app_job_route_parameter_module5(): assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.' + assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect in `_macros.html.' assert 'job_id' in inspect.getfullargspec(app.job).args, 'Have you added the correct parameters to the `job` function parameters list?' @pytest.mark.test_app_job_route_data_module5 diff --git a/tests/test_module7.py b/tests/test_module7.py index 64ae5b491..8203571a8 100644 --- a/tests/test_module7.py +++ b/tests/test_module7.py @@ -87,4 +87,4 @@ def test_app_redirect_to_employer_module7(): @pytest.mark.test_employer_review_button_module7 def test_employer_review_button_module7(): assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert 'review:employer_id:employer:id' in template_functions('employer', 'url_for'), 'In the `if` are you redirect back to the employer page?' + assert 'review:employer_id:employer:id' in template_functions('employer', 'url_for'), 'Does the `Create Review` button have the correct `href`?' From 5881ea11acf8a608bd970330e0c8d969a648b66f Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Mon, 17 Sep 2018 14:52:53 -0600 Subject: [PATCH 09/20] test: module3 & module6 fixes --- tests/test_module3.py | 2 ++ tests/test_module6.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_module3.py b/tests/test_module3.py index 120fb774c..8aaefb37a 100644 --- a/tests/test_module3.py +++ b/tests/test_module3.py @@ -69,6 +69,8 @@ def test_app_execute_sql_results_module3(): assert 'fetchone' in get_functions(app.execute_sql), 'Have you called the `fetchone` function in `execute_sql`?' assert 'commit' in get_functions(app.execute_sql), 'Have you called the `close` function in `execute_sql`?' assert 'close' in get_functions(app.execute_sql), 'Have you called the `close` function in `execute_sql`?' + assert len(get_statements(app.execute_sql)) >= 0, 'Have created an if statement in the `execute_sql` function?' + assert 'results' == get_statements(app.execute_sql)[0]['body/targets/id'], 'Have you assigned the `results` variable to `connection.commit()`?' with app.app.app_context(): results = app.execute_sql('SELECT * FROM job', single=True) assert type(results) != list, 'Have you create an if statement to only return one result in `one` is true?' diff --git a/tests/test_module6.py b/tests/test_module6.py index 74639e619..0a235e532 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -31,7 +31,7 @@ def test_employer_template_reviews_module6(): def test_employer_template_review_stars_module6(): assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' assert '_:range:1:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' - el = template_data('employer').select('.fa.fa_star.checked') + el = template_data('employer').select('.fa.fa-star.checked') assert len(el) == 1, 'Has the star `` been added to the `employer.html` template?' @pytest.mark.test_employer_template_review_details_module6 @@ -48,7 +48,7 @@ def test_app_employer_route_module6(): assert 'route:/employer/' in get_functions(app.employer) result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html')] assert len(result) == 1, 'Have you called the `render_template` function.' - assert 'employer:employer_id:job:employer_id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.' + assert 'employer:employer_id:job:employer_id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect in `_macros.html.' @pytest.mark.test_app_employer_route_employers_module6 def test_app_employer_route_employers_module6(): From 0f540c6ddd17334c652a0e88d19ac2bbe0f070b3 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Mon, 17 Sep 2018 15:05:59 -0600 Subject: [PATCH 10/20] test: module6 fix --- tests/test_module6.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_module6.py b/tests/test_module6.py index 0a235e532..f11a8d3c0 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -30,7 +30,7 @@ def test_employer_template_reviews_module6(): @pytest.mark.test_employer_template_review_stars_module6 def test_employer_template_review_stars_module6(): assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert '_:range:1:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' + assert '_:range:0:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' el = template_data('employer').select('.fa.fa-star.checked') assert len(el) == 1, 'Has the star `` been added to the `employer.html` template?' From 8b5dac474ef9724e33be3b4c14c8f7e45904a8ac Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Tue, 25 Sep 2018 14:42:16 -0600 Subject: [PATCH 11/20] test: return fix --- tests/test_module1.py | 3 +++ tests/test_module5.py | 4 +++- tests/test_module6.py | 4 +++- tests/test_module7.py | 2 ++ tests/utils.py | 11 +++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/test_module1.py b/tests/test_module1.py index 645d83c70..a9b090f24 100644 --- a/tests/test_module1.py +++ b/tests/test_module1.py @@ -29,6 +29,9 @@ def test_app_index_route_function_module1(): assert 'jobs' in dir(app), 'Have you created the `jobs` function?' result = [item for item in get_functions(app.jobs) if item.startswith('render_template:index.html')] assert len(result) == 1, 'Have you called the `render_template` function.' + return_values = get_functions_returns(app.jobs)[0] + assert return_values['value/args/s'] == 'index.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' + @pytest.mark.test_app_route_decoractors_module1 def test_app_route_decoractors_module1(): diff --git a/tests/test_module5.py b/tests/test_module5.py index 9931019c2..3153d64c4 100644 --- a/tests/test_module5.py +++ b/tests/test_module5.py @@ -16,6 +16,8 @@ def test_app_job_route_module5(): assert 'job' in dir(app), 'Have you created the `job` function?' result = [item for item in get_functions(app.job) if item.startswith('render_template:job.html')] assert len(result) == 1, 'Have you called the `render_template` function.' + return_values = get_functions_returns(app.job)[0] + assert return_values['value/args/s'] == 'job.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' @pytest.mark.test_app_job_route_decorator_module5 def test_app_job_route_decorator_module5(): @@ -25,7 +27,7 @@ def test_app_job_route_decorator_module5(): @pytest.mark.test_app_job_route_parameter_module5 def test_app_job_route_parameter_module5(): assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect in `_macros.html.' + assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.' assert 'job_id' in inspect.getfullargspec(app.job).args, 'Have you added the correct parameters to the `job` function parameters list?' @pytest.mark.test_app_job_route_data_module5 diff --git a/tests/test_module6.py b/tests/test_module6.py index f11a8d3c0..2c87e7b66 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -30,7 +30,7 @@ def test_employer_template_reviews_module6(): @pytest.mark.test_employer_template_review_stars_module6 def test_employer_template_review_stars_module6(): assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert '_:range:0:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' + assert '_:range:1:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' el = template_data('employer').select('.fa.fa-star.checked') assert len(el) == 1, 'Has the star `` been added to the `employer.html` template?' @@ -48,6 +48,8 @@ def test_app_employer_route_module6(): assert 'route:/employer/' in get_functions(app.employer) result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html')] assert len(result) == 1, 'Have you called the `render_template` function.' + return_values = get_functions_returns(app.employer)[0] + assert return_values['value/args/s'] == 'employer.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' assert 'employer:employer_id:job:employer_id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect in `_macros.html.' @pytest.mark.test_app_employer_route_employers_module6 diff --git a/tests/test_module7.py b/tests/test_module7.py index 8203571a8..8f0ef433c 100644 --- a/tests/test_module7.py +++ b/tests/test_module7.py @@ -20,6 +20,8 @@ def test_app_review_route_module7(): assert "route:/employer//review:methods:[{'s': 'GET'}, {'s': 'POST'}]" or "route:/employer//review:methods:[{'s': 'POST'}, {'s': 'GET'}]" in get_functions(app.review), 'Do you have a route decorator with the correct URL pattern and methods?' result = [item for item in get_functions(app.review) if item.startswith('render_template:review.html:employer_id:employer_id')] assert len(result) == 1, 'Have you called the `render_template` function with the correct arguments.' + return_values = get_functions_returns(app.review)[0] + assert return_values['value/args/s'] == 'review.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' @pytest.mark.test_app_review_post_request_check_module7 def test_app_review_post_request_check_module7(): diff --git a/tests/utils.py b/tests/utils.py index 18398e4a2..c4193673c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -56,6 +56,17 @@ def visit_Call(node): node_iter.visit(ast.parse(inspect.getsource(source))) return functions +def get_functions_returns(source): + returns = [] + + def visit_Return(node): + returns.append(build_dict(node)) + + node_iter = ast.NodeVisitor() + node_iter.visit_Return = visit_Return + node_iter.visit(ast.parse(inspect.getsource(source))) + return returns + def get_statements(source): statements = [] From c444dcacaec980e632b12a48948a91884037799c Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Wed, 26 Sep 2018 09:29:03 -0600 Subject: [PATCH 12/20] test: module6 and module7 return fixes --- tests/test_module6.py | 2 +- tests/test_module7.py | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/test_module6.py b/tests/test_module6.py index 2c87e7b66..ec3d78e6c 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -30,7 +30,7 @@ def test_employer_template_reviews_module6(): @pytest.mark.test_employer_template_review_stars_module6 def test_employer_template_review_stars_module6(): assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert '_:range:1:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' + assert '_:range:0:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' el = template_data('employer').select('.fa.fa-star.checked') assert len(el) == 1, 'Has the star `` been added to the `employer.html` template?' diff --git a/tests/test_module7.py b/tests/test_module7.py index 8f0ef433c..81f1ba390 100644 --- a/tests/test_module7.py +++ b/tests/test_module7.py @@ -20,8 +20,25 @@ def test_app_review_route_module7(): assert "route:/employer//review:methods:[{'s': 'GET'}, {'s': 'POST'}]" or "route:/employer//review:methods:[{'s': 'POST'}, {'s': 'GET'}]" in get_functions(app.review), 'Do you have a route decorator with the correct URL pattern and methods?' result = [item for item in get_functions(app.review) if item.startswith('render_template:review.html:employer_id:employer_id')] assert len(result) == 1, 'Have you called the `render_template` function with the correct arguments.' - return_values = get_functions_returns(app.review)[0] - assert return_values['value/args/s'] == 'review.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' + + return_values = get_functions_returns(app.review) + employer = { + "value/args/args/s": "employer", + "value/args/func/id": "url_for", + "value/args/keywords/arg": "employer_id", + "value/args/keywords/value/id": "employer_id", + "value/func/id": "redirect" + } + + render = { + "value/args/s": "review.html", + "value/func/id": "render_template", + "value/keywords/arg": "employer_id", + "value/keywords/value/id": "employer_id" + } + + assert render in return_values, 'Did you return the `render_template` call?' + assert employer in return_values, 'Did you return a call to `redirect` and `url_for`?' @pytest.mark.test_app_review_post_request_check_module7 def test_app_review_post_request_check_module7(): From e1fc4712ca0bd551f1ae91cc863404636c00cdef Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Thu, 27 Sep 2018 12:57:46 -0600 Subject: [PATCH 13/20] test: alternate sql statement --- tests/test_module6.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_module6.py b/tests/test_module6.py index ec3d78e6c..802b0f0b8 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -57,7 +57,8 @@ def test_app_employer_route_employers_module6(): assert 'employer' in dir(app), 'Have you created the `employer` function?' assert 'employer_id' in inspect.getfullargspec(app.employer).args, 'Have you added the correct parameters to the `employer` function parameter list?' execute_sql = 'execute_sql:SELECT * FROM employer WHERE id=?:employer_id:single:True' - assert execute_sql in get_functions(app.employer), '`execute_sql` has not been called or has the wrong parameters.' + execute_sql_alternate = 'execute_sql:SELECT * FROM employer WHERE id = ?:employer_id:single:True' + assert execute_sql in get_functions(app.employer) or execute_sql_alternate in get_functions(app.employer), '`execute_sql` has not been called or has the wrong parameters.' result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html:employer:employer')] assert len(result) == 1, 'Have you added `employer` to the `render_template` call.' From 9b40d9b94618bbf4f1d83271c2b4402f79cb3ac8 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Thu, 27 Sep 2018 13:02:58 -0600 Subject: [PATCH 14/20] docs: windows venv fix --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d9cca4f37..798fcf313 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Once that completes, also run this command from the same folder. Windows ``` -\venv\Scripts\activate.bat +venv\Scripts\activate.bat ``` macOS & Linux @@ -46,4 +46,4 @@ Every time you want to check your work locally you can type that command, and it ### Previewing Your Work -You can preview your work by running `flask run` in the root of your fork and then visit`http://localhost:5000` in your browser. \ No newline at end of file +You can preview your work by running `flask run` in the root of your fork and then visit`http://localhost:5000` in your browser. From 2c2445304d4c48eee6152f7ad051c09bdfb0abf9 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 24 Oct 2018 08:39:53 -0400 Subject: [PATCH 15/20] Add Dockerfile Signed-off-by: Will --- .dockerignore | 0 Dockerfile | 13 +++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..e69de29bb diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..409fcd54b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.6-alpine + +ENV APP_DIR /src/app/ + +RUN mkdir -p $APP_DIR + +COPY ./requirements.txt ${APP_DIR} + +WORKDIR ${APP_DIR} + +RUN ["pip", "install", "-r", "./requirements.txt"] + +COPY . . From 217ceda0b562181ba82e31df506f1a8e5a4a9d97 Mon Sep 17 00:00:00 2001 From: Jaron Thatcher Date: Thu, 21 Mar 2019 10:17:29 -0700 Subject: [PATCH 16/20] Create .gitattributes --- .gitattributes | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..fbf9358b0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.h text + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary From 2400d2b57a740e09aa7688af2504f7cc3910c7da Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 22 May 2019 10:12:30 -0400 Subject: [PATCH 17/20] Create projects user Signed-off-by: Will --- Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 409fcd54b..a47301c69 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,13 @@ FROM python:3.6-alpine -ENV APP_DIR /src/app/ +WORKDIR /src/app/ -RUN mkdir -p $APP_DIR - -COPY ./requirements.txt ${APP_DIR} - -WORKDIR ${APP_DIR} +COPY ./requirements.txt . RUN ["pip", "install", "-r", "./requirements.txt"] COPY . . + +RUN addgroup -S projects && adduser -S -H projects -G projects +RUN chown -R projects:projects /src/app +USER projects From 4dd07cbec1ccb914b5b34a49375132f5e3d8bac4 Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Wed, 29 Apr 2020 15:42:47 -0600 Subject: [PATCH 18/20] tests: ast fixes --- Dockerfile | 2 +- pytest.ini | 2 +- requirements.txt | 10 +- tests/test_module1.py | 90 +++++++++++++---- tests/test_module2.py | 61 +++++++++--- tests/test_module3.py | 184 ++++++++++++++++++++++++++-------- tests/test_module4.py | 176 ++++++++++++++++++++++++-------- tests/test_module5.py | 81 +++++++++++---- tests/test_module6.py | 226 ++++++++++++++++++++++++++++++++++-------- tests/test_module7.py | 183 ++++++++++++++++++++++++++-------- tests/utils.py | 168 +++++++++++++++++++++++-------- 11 files changed, 919 insertions(+), 264 deletions(-) diff --git a/Dockerfile b/Dockerfile index a47301c69..ac4a657d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.6-alpine +FROM python:3.8.2-alpine WORKDIR /src/app/ diff --git a/pytest.ini b/pytest.ini index d03e9e5d0..ccfcc2693 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,2 +1,2 @@ [pytest] -addopts = --json-report \ No newline at end of file +addopts = -rN --tb=short -p no:warnings diff --git a/requirements.txt b/requirements.txt index 7b72215f2..37f6ff698 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -beautifulsoup4==4.6.3 -Flask==1.0.2 -pytest==3.7.1 -pytest-json-report==0.7.0 -python-dotenv==0.9.1 \ No newline at end of file +beautifulsoup4==4.9.0 +Flask==1.1.2 +pytest==5.4.1 +pytest-json-report==1.2.1 +python-dotenv==0.13.0 diff --git a/tests/test_module1.py b/tests/test_module1.py index a9b090f24..7cdcaca1e 100644 --- a/tests/test_module1.py +++ b/tests/test_module1.py @@ -4,42 +4,92 @@ from .utils import * from jobs import app + @pytest.mark.test_app_import_flask_module1 def test_app_import_flask_module1(): - assert 'Flask' in dir(app), 'Have you imported the `Flask` class from `flask`?' - assert inspect.isclass(app.Flask), '`Flask` is not a class.' - assert 'render_template' in dir(app), '`render_template` has not been imported.' - assert inspect.isfunction(app.render_template), '`render_template` is not a function.' + flask_exists = "Flask" in dir(app) + assert flask_exists, "Have you imported the `Flask` class from `flask`?" + + flask_is_class = inspect.isclass(app.Flask) + assert flask_is_class, "`Flask` is not a class." + + render_template_exists = "render_template" in dir(app) + assert render_template_exists, "`render_template` has not been imported." + + render_template_is_function = inspect.isfunction(app.render_template) + assert render_template_is_function, "`render_template` is not a function." + @pytest.mark.test_app_create_flask_app_module1 def test_app_create_flask_app_module1(): - assert 'app' in dir(app), 'Have you created an instance of the `Flask` class called `app`?' - assert isinstance(app.app, app.Flask), '`app` is not an instance of the `Flask` class.' + flask_app = "app" in dir(app) + assert flask_app, "Have you created an instance of the `Flask` class called `app`?" + + flask_instance = isinstance(app.app, app.Flask) + assert flask_instance, "`app` is not an instance of the `Flask` class." + @pytest.mark.test_index_template_module1 def test_index_template_module1(): - assert os.path.isdir('jobs/templates'), 'The `templates` folder has not been created.' - assert template_exists('index'), 'The `index.html` template does not exist in the `templates` folder.' - assert template_find('index', 'h1', limit=1), "The `

` in the `index.html` template does not contain the contents 'Jobs'." - assert template_find('index', 'h1', limit=1)[0].text == 'Jobs', "The `

` in the `index.html` template does not contain the contents 'Jobs'." + template_dir = os.path.isdir("jobs/templates") + assert template_dir, "The `templates` folder has not been created." + + index_exists = template_exists("index") + assert ( + index_exists + ), "The `index.html` template does not exist in the `templates` folder." + + h1_exists = template_find("index", "h1", limit=1) + assert ( + h1_exists + ), "The `

` in the `index.html` template does not contain the contents 'Jobs'." + + h1_jobs = template_find("index", "h1", limit=1)[0].text == "Jobs" + assert ( + h1_jobs + ), "The `

` in the `index.html` template does not contain the contents 'Jobs'." + @pytest.mark.test_app_index_route_function_module1 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?' - result = [item for item in get_functions(app.jobs) if item.startswith('render_template:index.html')] - assert len(result) == 1, 'Have you called the `render_template` function.' + flask_app = "app" in dir(app) + assert flask_app, "Have you created an instance of the `Flask` class called `app`?" + + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + result = [ + item + for item in get_functions(app.jobs) + if item.startswith("render_template:index.html") + ] + result_len = len(result) == 1 + assert result_len, "Have you called the `render_template` function." + return_values = get_functions_returns(app.jobs)[0] - assert return_values['value/args/s'] == 'index.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' + return_exists = ( + return_values["value/args/s"] == "index.html" + and return_values["value/func/id"] == "render_template" + ) + assert return_exists, "Did you return the `render_template` call?" @pytest.mark.test_app_route_decoractors_module1 def test_app_route_decoractors_module1(): - assert 'app' in dir(app), 'Have you created an instance of the `Flask` class called `app`?' - assert template_exists('index'), 'The `index.html` template does not exist in the `templates` folder.' - assert 'jobs' in dir(app), 'Have you created the `jobs` function?' + flask_app = "app" in dir(app) + assert flask_app, "Have you created an instance of the `Flask` class called `app`?" + + index_exists = template_exists("index") + assert ( + index_exists + ), "The `index.html` template does not exist in the `templates` folder." + + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" rules = list_routes(app.app) + root_route = "jobs:GET,HEAD,OPTIONS:/" in rules + assert root_route, "Have you decorated the `jobs` function with the `/` route?" - assert 'jobs:GET,HEAD,OPTIONS:/' in rules, 'Have you decorated the `jobs` function with the `/` route?' - assert 'jobs:GET,HEAD,OPTIONS:/jobs' in rules, 'Have you decorated the `jobs` function with the `/jobs` route?' + jobs_route = "jobs:GET,HEAD,OPTIONS:/jobs" in rules + assert jobs_route, "Have you decorated the `jobs` function with the `/jobs` route?" diff --git a/tests/test_module2.py b/tests/test_module2.py index fe9d7f0ba..a1a5eeae3 100644 --- a/tests/test_module2.py +++ b/tests/test_module2.py @@ -3,33 +3,68 @@ from jobs import app from .utils import * -calls = template_functions('layout', 'url_for') +calls = template_functions("layout", "url_for") + @pytest.mark.test_layout_template_module2 def test_layout_template_module2(): - assert template_exists('layout'), 'The `layout.html` template does not exist in the `templates` folder.' + layout_exists = template_exists("layout") + assert ( + layout_exists + ), "The `layout.html` template does not exist in the `templates` folder." + @pytest.mark.test_add_bulma_css_framework_module2 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 calls, 'Looks like `bulma.min.css` is not linked in `layout.html`.' + layout_exists = template_exists("layout") + assert ( + layout_exists + ), "The `layout.html` template does not exist in the `templates` folder." + + css_exists = "static:filename:css/bulma.min.css" in calls + assert css_exists, "Looks like `bulma.min.css` is not linked in `layout.html`." + @pytest.mark.test_add_custom_css_module2 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 calls, 'Looks like `app.css` is not linked in `layout.html`.' + layout_exists = template_exists("layout") + assert ( + layout_exists + ), "The `layout.html` template does not exist in the `templates` folder." + + css_exists = "static:filename:css/app.css" in calls + assert css_exists, "Looks like `app.css` is not linked in `layout.html`." + @pytest.mark.test_add_fontawesome_module2 def test_add_fontawesome_module2(): - assert template_exists('layout'), 'The `layout.html` template does not exist in the `templates` folder.' + layout_exists = template_exists("layout") + assert ( + layout_exists + ), "The `layout.html` template does not exist in the `templates` folder." + attr = { - 'href': 'https://use.fontawesome.com/releases/v5.2.0/css/all.css', - 'rel': 'stylesheet' + "href": "https://use.fontawesome.com/releases/v5.2.0/css/all.css", + "rel": "stylesheet", } - assert template_soup('layout').find('link', attr), 'Looks like FontAwesome is not linked in `layout.html`.' + layout_link_exists = template_soup("layout").find("link", attr) + assert layout_link_exists, "Looks like FontAwesome is not linked in `layout.html`." + @pytest.mark.test_extend_base_template_module2 def test_extend_base_template_module2(): - assert template_exists('index'), 'The `index.html` template does not exist in the `templates` folder.' - assert template_exists('layout'), 'The `layout.html` template does not exist in the `templates` folder.' - assert 'layout.html' in template_extends('index'), 'The `index.html` template does not extend `layout.html`.' + index_exists = template_exists("index") + assert ( + index_exists + ), "The `index.html` template does not exist in the `templates` folder." + + layout_exists = template_exists("layout") + assert ( + layout_exists + ), "The `layout.html` template does not exist in the `templates` folder." + + index_extends = "layout.html" in template_extends("index") + assert index_extends, "The `index.html` template does not extend `layout.html`." + + content_block = "content" in template_block("index") + assert content_block, "Have you added a template `block` called `content`?" diff --git a/tests/test_module3.py b/tests/test_module3.py index 8aaefb37a..80c785c6e 100644 --- a/tests/test_module3.py +++ b/tests/test_module3.py @@ -4,87 +4,187 @@ from jobs import app from .utils import * + @pytest.mark.test_app_import_sqlite_module3 def test_app_import_sqlite_module3(): - assert 'sqlite3' in dir(app), 'Have you imported `sqlite`?' + sqlite_import = "sqlite3" in dir(app) + assert sqlite_import, "Have you imported `sqlite`?" + @pytest.mark.test_app_import_g_module3 def test_app_import_g_module3(): - assert 'g' in dir(app), 'Have you imported the `g` class from `flask`?' + g_import = "g" in dir(app) + assert g_import, "Have you imported the `g` class from `flask`?" + @pytest.mark.test_app_db_path_module3 def test_app_db_path_module3(): - assert 'PATH' in dir(app), 'Have you created a constant called `PATH`.' - assert app.PATH == 'db/jobs.sqlite', 'Have you created a constant called `PATH`?' + assert "PATH" in dir(app), "Have you created a constant called `PATH`?" + assert app.PATH == "db/jobs.sqlite", "Have you created a constant called `PATH`?" + @pytest.mark.test_app_open_connection_get_attribute_module3 def test_app_open_connection_get_attribute_module3(): - assert 'open_connection' in dir(app), 'Have you defined a function named `open_connection`.' - assert 'getattr:g:_connection:None' in get_functions(app.open_connection), 'Have you used the `getattr` function to get the global `_connection`?' + + open_connection = "open_connection" in dir(app) + assert open_connection, "Have you defined a function named `open_connection`?" + + getattr_g = "getattr:g:_connection:None" in get_functions(app.open_connection) + assert ( + getattr_g + ), "Have you used the `getattr` function to get the global `_connection`?" + @pytest.mark.test_app_open_connection_connection_module3 def test_app_open_connection_connection_module3(): - assert 'g' in dir(app), 'Have you imported the `g` class from `flask`?' - assert 'app' in dir(app), 'Have you created an instance of the `Flask` class called `app`?' - assert 'open_connection' in dir(app), 'Have you defined a function named `open_connection`.' + g_import = "g" in dir(app) + assert g_import, "Have you imported the `g` class from `flask`?" + + flask_app = "app" in dir(app) + assert flask_app, "Have you created an instance of the `Flask` class called `app`?" + + open_connection = "open_connection" in dir(app) + assert open_connection, "Have you defined a function named `open_connection`?" + with app.app.app_context(): app.open_connection() - assert hasattr(app.g, '_connection'), 'Did you assign the `_connection` attribute to `g`?' - _, _, db_name = app.g._connection.execute('PRAGMA database_list').fetchone() - assert os.path.join(os.getcwd(), 'db', 'jobs.sqlite') == db_name, 'Did you pass the `connect` function the `PATH` constant?' + + connection_exists = hasattr(app.g, "_connection") + assert connection_exists, "Did you assign the `_connection` attribute to `g`?" + + _, _, db_name = app.g._connection.execute("PRAGMA database_list").fetchone() + db_exists = os.path.join(os.getcwd(), "db", "jobs.sqlite") == db_name + assert db_exists, "Did you pass the `connect` function the `PATH` constant?" + @pytest.mark.test_app_open_connection_row_factory_module3 def test_app_open_connection_row_factory_module3(): - assert 'g' in dir(app), 'Have you imported the `g` class from `flask`?' - assert 'app' in dir(app), 'Have you created an instance of the `Flask` class called `app`?' - assert 'open_connection' in dir(app), 'Have you defined a function named `open_connection`.' + g_import = "g" in dir(app) + assert g_import, "Have you imported the `g` class from `flask`?" + + flask_app = "app" in dir(app) + assert flask_app, "Have you created an instance of the `Flask` class called `app`?" + + open_connection = "open_connection" in dir(app) + assert open_connection, "Have you defined a function named `open_connection`?" + with app.app.app_context(): db = app.open_connection() - assert isinstance(db, app.sqlite3.Connection), 'Are you returning the database connection?' - assert id(db.row_factory) == id(app.sqlite3.Row), 'Have you set the database `row_factory` to the sqlite3.Row class?' + return_connection = isinstance(db, app.sqlite3.Connection) + assert return_connection, "Are you returning the database connection?" + + row_factory = id(db.row_factory) == id(app.sqlite3.Row) + assert ( + row_factory + ), "Have you set the database `row_factory` to the sqlite3.Row class?" + @pytest.mark.test_app_execute_sql_module3 def test_app_execute_sql_module3(): - assert 'app' in dir(app), 'Have you created an instance of the `Flask` class called `app`?' - assert 'execute_sql' in dir(app), 'Have you defined a function named `execute_sql`.' - assert 'open_connection' in get_functions(app.execute_sql), 'Have you called the `open_connection` function in `execute_sql`?' + flask_app = "app" in dir(app) + assert flask_app, "Have you created an instance of the `Flask` class called `app`?" + + execute_sql_function = "execute_sql" in dir(app) + assert execute_sql_function, "Have you defined a function named `execute_sql`?" + + open_call = "open_connection" in get_functions(app.execute_sql) + assert open_call, "Have you called the `open_connection` function in `execute_sql`?" + @pytest.mark.test_app_execute_sql_parameters_module3 def test_app_execute_sql_parameters_module3(): - assert 'execute_sql' in dir(app), 'Have you defined a function named `execute_sql`.' + execute_sql_function = "execute_sql" in dir(app) + assert execute_sql_function, "Have you defined a function named `execute_sql`?" + parameters = inspect.getfullargspec(app.execute_sql) - assert len(parameters.args) == 4, 'Have you added parameters to the `execute_sql` function.' - assert parameters.args[0] == 'sql' and parameters.args[1] == 'values' and parameters.args[2] == 'commit' and parameters.args[3] == 'single', 'Have you added the correct parameters to the `execute_sql` function parameters list?' - assert parameters.defaults[0] == () and parameters.defaults[1] == False and parameters.defaults[2] == False, 'Do the `args` and `one` parameters have the correct defaults in the `execute_sql` function parameters list?' + arg_len = len(parameters.args) == 4 + assert arg_len, "Have you added parameters to the `execute_sql` function?" + + args = ( + parameters.args[0] == "sql" + and parameters.args[1] == "values" + and parameters.args[2] == "commit" + and parameters.args[3] == "single" + ) + assert ( + args + ), "Have you added the correct parameters to the `execute_sql` function parameters list?" + + defaults = ( + parameters.defaults[0] == () + and parameters.defaults[1] == False + and parameters.defaults[2] == False + ) + assert ( + defaults + ), "Do the `args` and `one` parameters have the correct defaults in the `execute_sql` function parameters list?" + @pytest.mark.test_app_execute_sql_execute_module3 def test_app_execute_sql_execute_module3(): - assert 'execute_sql' in dir(app), 'Have you defined a function named `execute_sql`.' - assert 'execute:sql:values' in get_functions(app.execute_sql), 'Have you called the `execute` function in `execute_sql`?' + execute_sql_function = "execute_sql" in dir(app) + assert execute_sql_function, "Have you defined a function named `execute_sql`?" + + execute_call = "execute:sql:values" in get_functions(app.execute_sql) + assert execute_call, "Have you called the `execute` function in `execute_sql`?" + @pytest.mark.test_app_execute_sql_results_module3 def test_app_execute_sql_results_module3(): - assert 'execute_sql' in dir(app), 'Have you defined a function named `execute_sql`.' - assert 'fetchall' in get_functions(app.execute_sql), 'Have you called the `fetchall` function in `execute_sql`?' - assert 'fetchone' in get_functions(app.execute_sql), 'Have you called the `fetchone` function in `execute_sql`?' - assert 'commit' in get_functions(app.execute_sql), 'Have you called the `close` function in `execute_sql`?' - assert 'close' in get_functions(app.execute_sql), 'Have you called the `close` function in `execute_sql`?' - assert len(get_statements(app.execute_sql)) >= 0, 'Have created an if statement in the `execute_sql` function?' - assert 'results' == get_statements(app.execute_sql)[0]['body/targets/id'], 'Have you assigned the `results` variable to `connection.commit()`?' + execute_sql_function = "execute_sql" in dir(app) + assert execute_sql_function, "Have you defined a function named `execute_sql`?" + + fetchall = "fetchall" in get_functions(app.execute_sql) + assert fetchall, "Have you called the `fetchall` function in `execute_sql`?" + + fetchone = "fetchone" in get_functions(app.execute_sql) + assert fetchone, "Have you called the `fetchone` function in `execute_sql`?" + + commit = "commit" in get_functions(app.execute_sql) + assert commit, "Have you called the `commit` function in `execute_sql`?" + + close = "close" in get_functions(app.execute_sql) + assert close, "Have you called the `close` function in `execute_sql`?" + + if_statement = len(get_statements(app.execute_sql)) >= 0 + assert if_statement, "Have created an if statement in the `execute_sql` function?" + + results_exists = "results" == get_statements(app.execute_sql)[0]["body/targets/id"] + assert ( + results_exists + ), "Have you assigned the `results` variable to `connection.commit()`?" + with app.app.app_context(): - results = app.execute_sql('SELECT * FROM job', single=True) - assert type(results) != list, 'Have you create an if statement to only return one result in `one` is true?' + results = type(app.execute_sql("SELECT * FROM job", single=True)) != list + assert ( + results + ), "Have you create an if statement to only return one result in `one` is true?" + @pytest.mark.test_app_close_connection_module3 def test_app_close_connection_module3(): - assert 'close_connection' in dir(app), 'Have you defined a function named `close_connection`.' - assert 'getattr:g:_connection:None' in get_functions(app.open_connection), 'Have you used the `getattr` function to get the global `_connection`?' - assert 'close' in get_functions(app.execute_sql), 'Have you called the `close` function in `execute_sql`?' + close_connection = "close_connection" in dir(app) + assert close_connection, "Have you defined a function named `close_connection`?" + + getattr_g = "getattr:g:_connection:None" in get_functions(app.open_connection) + assert ( + getattr_g + ), "Have you used the `getattr` function to get the global `_connection`?" + + close = "close" in get_functions(app.close_connection) + assert close, "Have you called the `close` function in `close_connection`?" + @pytest.mark.test_app_close_connection_decorator_module3 def test_app_close_connection_decorator_module3(): - assert 'close_connection' in dir(app), 'Have you defined a function named `close_connection`.' - decorators = get_decorators(app.close_connection)['close_connection'] - assert len(decorators) == 1, 'Have you added the correct decorator to `close_connection`.' + close_connection = "close_connection" in dir(app) + assert close_connection, "Have you defined a function named `close_connection`?" + + decorators = get_decorators(app.close_connection)["close_connection"] + + decorators_len = len(decorators) == 1 + assert decorators_len, "Have you added the correct decorator to `close_connection`?" + decorator = decorators[0][0] - assert decorator == 'teardown_appcontext', 'Does `close_connection` have a `teardown_appcontext` decorator?' \ No newline at end of file + teardown = decorator == "teardown_appcontext" + assert teardown, "Does `close_connection` have a `teardown_appcontext` decorator?" diff --git a/tests/test_module4.py b/tests/test_module4.py index 4df565230..7887d41ca 100644 --- a/tests/test_module4.py +++ b/tests/test_module4.py @@ -4,77 +4,175 @@ from jobs import app from .utils import * + @pytest.mark.test_template_macros_module4 def test_template_macros_module4(): - assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + @pytest.mark.test_show_job_macro_definition_module4 def test_show_job_macro_definition_module4(): - 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?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + show_job = "show_job:job" in template_macros("_macros") + assert ( + show_job + ), "Have you created the `show_job` macro and added the correct parameter?" + @pytest.mark.test_show_job_macro_html_module4 def test_show_job_macro_html_module4(): - 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?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "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") + + copied = len(p) == 1 and len(div) == 1 + assert ( + copied + ), "Has the `HTML` from `templates.html` been copied to the `show_job` macro?" + @pytest.mark.test_show_job_macro_header_module4 def test_show_job_macro_header_module4(): - assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.' - assert 'job:title' in template_variables('_macros'), 'Looks like the job title link does not have content.' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + job_title = "job:title" in template_variables("_macros") + assert job_title, "Looks like the job title link does not have content." + @pytest.mark.test_show_job_macro_body_module4 def test_show_job_macro_body_module4(): - assert template_exists('_macros'), 'The `_macros.html` template does not exist in the `templates` folder.' - assert 'job:employer_name' in template_variables('_macros'), 'Are you showing the employer name?' - assert 'job:salary' in template_variables('_macros'), 'Are you showing the job salary?' - assert 'job:description' in template_variables('_macros'), 'Are you showing the job description?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + employer_name = "job:employer_name" in template_variables("_macros") + assert employer_name, "Are you showing the employer name?" + + salary = "job:salary" in template_variables("_macros") + assert salary, "Are you showing the job salary?" + + description = "job:description" in template_variables("_macros") + assert description, "Are you showing the job description?" + @pytest.mark.test_show_jobs_macro_definition_module4 def test_show_jobs_macro_definition_module4(): - 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?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + show_jobs = "show_jobs:jobs" in template_macros("_macros") + assert ( + show_jobs + ), "Have you created the `show_jobs` macro and added the correct parameter?" + @pytest.mark.test_show_jobs_macro_for_loop_module4 def test_show_jobs_macro_for_loop_module4(): - 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?' - html = template_macro_soup('_macros', 'show_jobs') - div = html.select('div.columns.is-multiline') - assert len(div) == 1, 'Has a `
` with classes of `columns` and `is-multiline` been added to the `show_jobs` macro?' - assert 'job:jobs' in show_jobs_for(), 'Does the `show_jobs` macro contain a `for` loop?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + show_jobs = "show_jobs:jobs" in template_macros("_macros") + assert ( + show_jobs + ), "Have you created the `show_jobs` macro and added the correct parameter?" + + div = template_macro_soup("_macros", "show_jobs").select("div.columns.is-multiline") + div_len = len(div) == 1 + assert ( + div_len + ), "Has a `
` with classes of `columns` and `is-multiline` been added to the `show_jobs` macro?" + + show_job_for = "job:jobs" in show_jobs_for() + assert show_job_for, "Does the `show_jobs` macro contain a `for` loop?" + @pytest.mark.test_show_jobs_macro_for_loop_body_module4 def test_show_jobs_macro_for_loop_body_module4(): - 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?' - html = template_macro_soup('_macros', 'show_jobs') - div = html.select('div.column.is-half') - assert len(div) == 1, 'Has a `
` with classes of `column` and `is-half` been added to the `show_jobs` macro `for` loop body?' - assert 'show_job:job' in show_jobs_for(), 'Does the `show_jobs` macro call `show_job`?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + show_jobs = "show_jobs:jobs" in template_macros("_macros") + assert ( + show_jobs + ), "Have you created the `show_jobs` macro and added the correct parameter?" + + div = template_macro_soup("_macros", "show_jobs").select("div.column.is-half") + div_len = len(div) == 1 + assert ( + div_len + ), "Has a `
` with classes of `column` and `is-half` been added to the `show_jobs` macro `for` loop body?" + + show_job_call = "show_job:job" in show_jobs_for() + assert show_job_call, "Does the `show_jobs` macro call `show_job`?" + @pytest.mark.test_import_macros_module4 def test_import_macros_module4(): - 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 `layout.html`?' + macros_exists = template_exists("_macros") + assert ( + macros_exists + ), "The `_macros.html` template does not exist in the `templates` folder." + + import_exists = "_macros.html:show_job:show_jobs:True" == template_import("layout") + assert import_exists, "Have you imported `_macros.html` in `layout.html`?" + @pytest.mark.test_index_template_module4 def test_index_template_module4(): - assert template_exists('index'), 'The `index.html` template does not exist in the `templates` folder.' - 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?' + index_exists = template_exists("index") + assert ( + index_exists + ), "The `index.html` template does not exist in the `templates` folder." + + el = len(template_data("index").select(".columns .column.is-one-fifth")) == 1 + assert ( + el + ), "Has the `HTML` from `templates.html` been copied to the `index.html` template?" + @pytest.mark.test_display_all_jobs_module4 def test_display_all_jobs_module4(): - assert template_exists('index'), 'The `index.html` template does not exist in the `templates` folder.' - assert 'show_jobs:jobs' in template_functions('index', 'show_jobs'), 'Have you call the `show_jobs` macro in the `index.html` file?' + index_exists = template_exists("index") + assert ( + index_exists + ), "The `index.html` template does not exist in the `templates` folder." + + show_jobs_call = "show_jobs:jobs" in template_functions("index", "show_jobs") + assert ( + show_jobs_call + ), "Have you call the `show_jobs` macro in the `index.html` file?" + @pytest.mark.test_app_jobs_route_jobs_module4 def test_app_jobs_route_jobs_module4(): - assert 'jobs' in dir(app), 'Have you created the `jobs` function?' - execute_sql = 'execute_sql: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 execute_sql in get_functions(app.jobs), '`execute_sql` 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.' + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + execute_sql = "execute_sql: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" + sql_exists = execute_sql in get_functions(app.jobs) + assert sql_exists, "`execute_sql` has not been called or has the wrong parameters." + + new_render_call = "render_template:index.html:jobs:jobs" in get_functions(app.jobs) + assert new_render_call, "Have you added `jobs` to the `render_template` call." diff --git a/tests/test_module5.py b/tests/test_module5.py index 3153d64c4..6b3338b06 100644 --- a/tests/test_module5.py +++ b/tests/test_module5.py @@ -4,39 +4,84 @@ from jobs import app from .utils import * + @pytest.mark.test_app_job_template_module5 def test_app_job_template_module5(): - assert template_exists('job'), 'The `job.html` template does not exist in the `templates` folder.' - assert 'layout.html' in template_extends('job'), 'The `job.html` template does not extend `layout.html`.' - assert 'content' in template_block('job'), 'Have you added a template `block` called `content`?' - assert 'show_job:job' in template_functions('job', 'show_job'), 'Have you call the `show_job` macro in the `job.html` file?' + + job_exists = template_exists("job") + assert ( + job_exists + ), "The `job.html` template does not exist in the `templates` folder." + + job_extends = "layout.html" in template_extends("job") + assert job_extends, "The `job.html` template does not extend `layout.html`." + + content_block = "content" in template_block("job") + assert content_block, "Have you added a template `block` called `content`?" + + show_job = "show_job:job" in template_functions("job", "show_job") + assert show_job, "Have you call the `show_job` macro in the `job.html` file?" + @pytest.mark.test_app_job_route_module5 def test_app_job_route_module5(): - assert 'job' in dir(app), 'Have you created the `job` function?' - result = [item for item in get_functions(app.job) if item.startswith('render_template:job.html')] - assert len(result) == 1, 'Have you called the `render_template` function.' + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + result = [ + item + for item in get_functions(app.job) + if item.startswith("render_template:job.html") + ] + result_len = len(result) == 1 + assert result_len, "Have you called the `render_template` function." + return_values = get_functions_returns(app.job)[0] - assert return_values['value/args/s'] == 'job.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' + return_exists = ( + return_values["value/args/s"] == "job.html" + and return_values["value/func/id"] == "render_template" + ) + assert return_exists, "Did you return the `render_template` call?" + @pytest.mark.test_app_job_route_decorator_module5 def test_app_job_route_decorator_module5(): - assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'route:/job/' in get_functions(app.job), 'Have you added a `job_id` parameter to the job function' + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + job_id = "route:/job/" in get_functions(app.job) + assert job_id, "Have you added a `job_id` parameter to the job function" + @pytest.mark.test_app_job_route_parameter_module5 def test_app_job_route_parameter_module5(): - assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'job:job_id:job:id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect.' - assert 'job_id' in inspect.getfullargspec(app.job).args, 'Have you added the correct parameters to the `job` function parameters list?' + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + job_href = "job:job_id:job:id" in template_functions("_macros", "url_for") + assert job_href, "Looks like the job title link `href` is incorrect." + + job_id = "job_id" in inspect.getfullargspec(app.job).args + assert ( + job_id + ), "Have you added the correct parameters to the `job` function parameters list?" + @pytest.mark.test_app_job_route_data_module5 def test_app_job_route_data_module5(): - assert 'job' in dir(app), 'Have you created the `job` function?' - execute_sql = 'execute_sql: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 WHERE job.id = ?:job_id:single:True' - assert execute_sql in get_functions(app.job), '`execute_sql` has not been called or has the wrong parameters.' + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + execute_sql = "execute_sql: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 WHERE job.id = ?:job_id:single:True" + result = [item for item in get_functions(app.job) if item.startswith(execute_sql)] + result_len = len(result) == 1 + assert result_len, "`execute_sql` has not been called or has the wrong parameters." + @pytest.mark.test_app_job_route_pass_data_module5 def test_app_job_route_pass_data_module5(): - assert 'job' in dir(app), 'Have you created the `job` function?' - assert 'render_template:job.html:job:job' in get_functions(app.job), 'Have you added `job` to the `render_template` call.' + jobs_function = "jobs" in dir(app) + assert jobs_function, "Have you created the `jobs` function?" + + new_render = "render_template:job.html:job:job" in get_functions(app.job) + assert new_render, "Have you added `job` to the `render_template` call." diff --git a/tests/test_module6.py b/tests/test_module6.py index 802b0f0b8..0dd3a5de1 100644 --- a/tests/test_module6.py +++ b/tests/test_module6.py @@ -4,76 +4,216 @@ from jobs import app from .utils import * + @pytest.mark.test_employer_template_module6 def test_employer_template_module6(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - el = template_data('employer').select('.box .media .media-content .content') - assert len(el) == 1, 'Has the `HTML` from `templates.html` been copied to the `employer.html` template?' - assert 'layout.html' in template_extends('employer'), 'The `employer.html` template does not extend `layout.html`.' + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + el = ( + len(template_data("employer").select(".box .media .media-content .content")) + == 1 + ) + assert ( + el + ), "Has the `HTML` from `templates.html` been copied to the `employer.html` template?" + + employer_extends = "layout.html" in template_extends("employer") + assert ( + employer_extends + ), "The `employer.html` template does not extend `layout.html`." + @pytest.mark.test_employer_template_details_module6 def test_employer_template_details_module6(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert 'employer:name' in template_variables('employer'), "Looks like the `employer['name']` is not present in the template." - assert 'employer:description' in template_variables('employer'), "Looks like the `employer['description']` is not present in the template." + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + employer_name = "employer:name" in template_variables("employer") + assert ( + employer_name + ), "Looks like the `employer['name']` is not present in the template." + + employer_description = "employer:description" in template_variables("employer") + assert ( + employer_description + ), "Looks like the `employer['description']` is not present in the template." + @pytest.mark.test_employer_template_all_jobs_module6 def test_employer_template_all_jobs_module6(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert 'show_jobs:jobs' in template_functions('employer', 'show_jobs'), 'Have you called the `show_jobs` macro in the `employer.html` file?' + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + show_job = "show_jobs:jobs" in template_functions("employer", "show_jobs") + assert ( + show_job + ), "Have you called the `show_jobs` macro in the `employer.html` file?" + @pytest.mark.test_employer_template_reviews_module6 def test_employer_template_reviews_module6(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert 'review:reviews' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + reviews_for = "review:reviews" in employer_for() + assert reviews_for, "Have you created a `for` loop that cycles through `reviews`?" + @pytest.mark.test_employer_template_review_stars_module6 def test_employer_template_review_stars_module6(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert '_:range:0:review:rating' in employer_for(), 'Have you created a `for` loop that cycles through `reviews`?' - el = template_data('employer').select('.fa.fa-star.checked') - assert len(el) == 1, 'Has the star `` been added to the `employer.html` template?' + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + review_range = "_:range:0:review:rating" in employer_for() + assert review_range, "Have you created a `for` loop that cycles through `reviews`?" + el = len(template_data("employer").select(".fa.fa-star.checked")) == 1 + assert el, "Has the star `` been added to the `employer.html` template?" + @pytest.mark.test_employer_template_review_details_module6 def test_employer_template_review_details_module6(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert 'review:title' in template_variables('employer'), "Looks like the `review['title']` is not present in the template." - assert 'review:status' in template_variables('employer'), "Looks like the `review['status']` is not present in the template." - assert 'review:date' in template_variables('employer'), "Looks like the `review['date']` is not present in the template." - assert 'review:review' in template_variables('employer'), "Looks like the `review['review']` is not present in the template." + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + review_title = "review:title" in template_variables("employer") + assert ( + review_title + ), "Looks like the `review['title']` is not present in the template." + + review_status = "review:status" in template_variables("employer") + assert ( + review_status + ), "Looks like the `review['status']` is not present in the template." + + review_date = "review:date" in template_variables("employer") + assert ( + review_date + ), "Looks like the `review['date']` is not present in the template." + + review_review = "review:review" in template_variables("employer") + assert ( + review_review + ), "Looks like the `review['review']` is not present in the template." + @pytest.mark.test_app_employer_route_module6 def test_app_employer_route_module6(): - assert 'employer' in dir(app), 'Have you created the `employer` function?' - assert 'route:/employer/' in get_functions(app.employer) - result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html')] - assert len(result) == 1, 'Have you called the `render_template` function.' + employer_function = "employer" in dir(app) + assert employer_function, "Have you created the `employer` function?" + + employer_route = "route:/employer/" in get_functions(app.employer) + assert employer_route, "Does `employer` function have the correct route?" + + result = [ + item + for item in get_functions(app.employer) + if item.startswith("render_template:employer.html") + ] + result_len = len(result) == 1 + assert result_len, "Have you called the `render_template` function." + return_values = get_functions_returns(app.employer)[0] - assert return_values['value/args/s'] == 'employer.html' and return_values['value/func/id'] == 'render_template', 'Did you return the `render_template` call?' - assert 'employer:employer_id:job:employer_id' in template_functions('_macros', 'url_for'), 'Looks like the job title link `href` is incorrect in `_macros.html.' + return_exists = ( + return_values["value/args/s"] == "employer.html" + and return_values["value/func/id"] == "render_template" + ) + assert return_exists, "Did you return the `render_template` call?" + + job_title = "employer:employer_id:job:employer_id" in template_functions( + "_macros", "url_for" + ) + assert ( + job_title + ), "Looks like the job title link `href` is incorrect in `_macros.html." + @pytest.mark.test_app_employer_route_employers_module6 def test_app_employer_route_employers_module6(): - assert 'employer' in dir(app), 'Have you created the `employer` function?' - assert 'employer_id' in inspect.getfullargspec(app.employer).args, 'Have you added the correct parameters to the `employer` function parameter list?' - execute_sql = 'execute_sql:SELECT * FROM employer WHERE id=?:employer_id:single:True' - execute_sql_alternate = 'execute_sql:SELECT * FROM employer WHERE id = ?:employer_id:single:True' - assert execute_sql in get_functions(app.employer) or execute_sql_alternate in get_functions(app.employer), '`execute_sql` has not been called or has the wrong parameters.' - result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html:employer:employer')] - assert len(result) == 1, 'Have you added `employer` to the `render_template` call.' + employer_function = "employer" in dir(app) + assert employer_function, "Have you created the `employer` function?" + + args = "employer_id" in inspect.getfullargspec(app.employer).args + assert ( + args + ), "Have you added the correct parameters to the `employer` function parameter list?" + + execute_sql = ( + "execute_sql:SELECT * FROM employer WHERE id=?:employer_id:single:True" + ) + execute_sql_alternate = ( + "execute_sql:SELECT * FROM employer WHERE id = ?:employer_id:single:True" + ) + + result_sql = [ + item for item in get_functions(app.employer) if item.startswith(execute_sql) + ] + result_sql_len = len(result_sql) == 1 + + result_sql_alternate = [ + item + for item in get_functions(app.employer) + if item.startswith(execute_sql_alternate) + ] + result_sql_alternate_len = len(result_sql_alternate) == 1 + + sql_exists = result_sql_len or result_sql_alternate_len + assert sql_exists, "`execute_sql` has not been called or has the wrong parameters." + + result = [ + item + for item in get_functions(app.employer) + if item.startswith("render_template:employer.html:employer:employer") + ] + result_len = len(result) == 1 + assert result_len, "Have you added `employer` to the `render_template` call." + @pytest.mark.test_app_employer_route_jobs_module6 def test_app_employer_route_jobs_module6(): - assert 'employer' in dir(app), 'Have you created the `employer` function?' - execute_sql = 'execute_sql:SELECT job.id, job.title, job.description, job.salary FROM job JOIN employer ON employer.id = job.employer_id WHERE employer.id = ?:employer_id' - assert execute_sql in get_functions(app.employer), '`execute_sql` has not been called or has the wrong parameters.' - result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html:employer:employer:jobs:jobs')] - assert len(result) == 1, 'Have you added `jobs` to the `render_template` call.' + employer_function = "employer" in dir(app) + assert employer_function, "Have you created the `employer` function?" + + execute_sql = "execute_sql:SELECT job.id, job.title, job.description, job.salary FROM job JOIN employer ON employer.id = job.employer_id WHERE employer.id = ?:employer_id" + sql_exists = execute_sql in get_functions(app.employer) + assert sql_exists, "`execute_sql` has not been called or has the wrong parameters." + + result = [ + item + for item in get_functions(app.employer) + if item.startswith("render_template:employer.html:employer:employer:jobs:jobs") + ] + result_len = len(result) == 1 + assert result_len, "Have you added `jobs` to the `render_template` call." + @pytest.mark.test_app_employer_route_reviews_module6 def test_app_employer_route_reviews_module6(): - assert 'employer' in dir(app), 'Have you created the `employer` function?' - execute_sql = 'execute_sql:SELECT review, rating, title, date, status FROM review JOIN employer ON employer.id = review.employer_id WHERE employer.id = ?:employer_id' - assert execute_sql in get_functions(app.employer), '`execute_sql` has not been called or has the wrong parameters.' - result = [item for item in get_functions(app.employer) if item.startswith('render_template:employer.html:employer:employer:jobs:jobs:reviews:reviews')] - assert len(result) == 1, 'Have you added `reviews` to the `render_template` call.' + employer_function = "employer" in dir(app) + assert employer_function, "Have you created the `employer` function?" + + execute_sql = "execute_sql:SELECT review, rating, title, date, status FROM review JOIN employer ON employer.id = review.employer_id WHERE employer.id = ?:employer_id" + sql_exists = execute_sql in get_functions(app.employer) + assert sql_exists, "`execute_sql` has not been called or has the wrong parameters." + + result = [ + item + for item in get_functions(app.employer) + if item.startswith( + "render_template:employer.html:employer:employer:jobs:jobs:reviews:reviews" + ) + ] + result_len = len(result) == 1 + assert result_len, "Have you added `reviews` to the `render_template` call." diff --git a/tests/test_module7.py b/tests/test_module7.py index 81f1ba390..78b77ea75 100644 --- a/tests/test_module7.py +++ b/tests/test_module7.py @@ -5,21 +5,59 @@ from jobs import app from .utils import * + @pytest.mark.test_review_template_module7 def test_review_template_module7(): - assert template_exists('review'), 'The `review.html` template does not exist in the `templates` folder.' - el = template_data('review').select('.field.is-grouped .control .button.is-text') - assert len(el) == 1, 'Has the `HTML` from `templates.html` been copied to the `review.html` template?' - assert 'layout.html' in template_extends('review'), 'The `review.html` template does not extend `layout.html`.' - assert 'employer:employer_id:employer_id' in template_functions('review', 'url_for'), 'Have you called the `url_for` function in the `review.html` file?' + review_exists = template_exists("review") + assert ( + review_exists + ), "The `review.html` template does not exist in the `templates` folder." + + el = template_data("review").select(".field.is-grouped .control .button.is-text") + el_len = len(el) == 1 + assert ( + el_len + ), "Has the `HTML` from `templates.html` been copied to the `review.html` template?" + + review_extends = "layout.html" in template_extends("review") + assert review_extends, "The `review.html` template does not extend `layout.html`." + + review_url_for = "employer:employer_id:employer_id" in template_functions( + "review", "url_for" + ) + assert ( + review_url_for + ), "Have you called the `url_for` function in the `review.html` file?" + @pytest.mark.test_app_review_route_module7 def test_app_review_route_module7(): - assert 'review' in dir(app), 'Have you created the `review` function?' - assert 'employer_id' in inspect.getfullargspec(app.review).args, 'Have you added the correct parameters to the `review` function parameter list?' - assert "route:/employer//review:methods:[{'s': 'GET'}, {'s': 'POST'}]" or "route:/employer//review:methods:[{'s': 'POST'}, {'s': 'GET'}]" in get_functions(app.review), 'Do you have a route decorator with the correct URL pattern and methods?' - result = [item for item in get_functions(app.review) if item.startswith('render_template:review.html:employer_id:employer_id')] - assert len(result) == 1, 'Have you called the `render_template` function with the correct arguments.' + review_function = "review" in dir(app) + assert review_function, "Have you created the `review` function?" + + employer_id = "employer_id" in inspect.getfullargspec(app.review).args + assert ( + employer_id + ), "Have you added the correct parameters to the `review` function parameter list?" + + route_pattern = ( + "route:/employer//review:methods:[{'s': 'GET'}, {'s': 'POST'}]" + or "route:/employer//review:methods:[{'s': 'POST'}, {'s': 'GET'}]" + in get_functions(app.review) + ) + assert ( + route_pattern + ), "Do you have a route decorator with the correct URL pattern and methods?" + + result = [ + item + for item in get_functions(app.review) + if item.startswith("render_template:review.html:employer_id:employer_id") + ] + result_len = len(result) == 1 + assert ( + result_len + ), "Have you called the `render_template` function with the correct arguments." return_values = get_functions_returns(app.review) employer = { @@ -27,53 +65,75 @@ def test_app_review_route_module7(): "value/args/func/id": "url_for", "value/args/keywords/arg": "employer_id", "value/args/keywords/value/id": "employer_id", - "value/func/id": "redirect" + "value/func/id": "redirect", } - render = { "value/args/s": "review.html", "value/func/id": "render_template", "value/keywords/arg": "employer_id", - "value/keywords/value/id": "employer_id" + "value/keywords/value/id": "employer_id", } - assert render in return_values, 'Did you return the `render_template` call?' - assert employer in return_values, 'Did you return a call to `redirect` and `url_for`?' + render_return = render in return_values + assert render_return, "Did you return the `render_template` call?" + + employer_return = employer in return_values + assert employer_return, "Did you return a call to `redirect` and `url_for`?" + @pytest.mark.test_app_review_post_request_check_module7 def test_app_review_post_request_check_module7(): - assert 'review' in dir(app), 'Have you created the `review` function?' - assert 'datetime' in dir(app), '`datetime` has not been imported.' + review_function = "review" in dir(app) + assert review_function, "Have you created the `review` function?" + + datetime_exists = "datetime" in dir(app) + assert datetime_exists, "`datetime` has not been imported." if_statement = get_statements(app.review)[0] - body = if_statement['body'] - post = 'test/comparators/s' in if_statement and 'POST' == if_statement['test/comparators/s'] - method = 'test/left/attr' in if_statement and 'method' == if_statement['test/left/attr'] - request = 'test/left/value/id' in if_statement and 'request' == if_statement['test/left/value/id'] - eq = 'test/ops/node_type' in if_statement and 'Eq' == if_statement['test/ops/node_type'] + body = if_statement["body"] + for item in body: + item.pop("type_comment", None) + item.pop("value/slice/value/value", None) + item.pop("value/args/value", None) + + post = ( + "test/comparators/s" in if_statement + and "POST" == if_statement["test/comparators/s"] + ) + method = ( + "test/left/attr" in if_statement and "method" == if_statement["test/left/attr"] + ) + request = ( + "test/left/value/id" in if_statement + and "request" == if_statement["test/left/value/id"] + ) + eq = ( + "test/ops/node_type" in if_statement + and "Eq" == if_statement["test/ops/node_type"] + ) review = { "targets/id": "review", "value/slice/value/s": "review", "value/value/attr": "form", - "value/value/value/id": "request" + "value/value/value/id": "request", } rating = { "targets/id": "rating", "value/slice/value/s": "rating", "value/value/attr": "form", - "value/value/value/id": "request" + "value/value/value/id": "request", } title = { "targets/id": "title", "value/slice/value/s": "title", "value/value/attr": "form", - "value/value/value/id": "request" + "value/value/value/id": "request", } status = { "targets/id": "status", "value/slice/value/s": "status", "value/value/attr": "form", - "value/value/value/id": "request" + "value/value/value/id": "request", } date = { "targets/id": "date", @@ -81,29 +141,70 @@ def test_app_review_post_request_check_module7(): "value/func/attr": "strftime", "value/func/value/func/attr": "now", "value/func/value/func/value/attr": "datetime", - "value/func/value/func/value/value/id": "datetime" + "value/func/value/func/value/value/id": "datetime", } - assert post and method and request and eq, 'Do you have an `if` statement to test if the request method equals "POST?' - assert review in body, 'Have you created the `review` variable?' - assert rating in body, 'Have you created the `rating` variable?' - assert title in body, 'Have you created the `title` variable?' - assert status in body, 'Have you created the `status` variable?' - assert date in body, 'Have you created the `date` variable?' + + post_if = post and method and request and eq + assert ( + post_if + ), 'Do you have an `if` statement to test if the request method equals "POST?' + + body_review = review in body + assert body_review, "Have you created the `review` variable?" + + body_rating = rating in body + assert body_rating, "Have you created the `rating` variable?" + + body_title = title in body + assert body_title, "Have you created the `title` variable?" + + body_status = status in body + assert body_status, "Have you created the `status` variable?" + + body_date = date in body + assert body_date, "Have you created the `date` variable?" + @pytest.mark.test_app_review_insert_review_module7 def test_app_review_insert_review_module7(): - assert 'review' in dir(app), 'Have you created the `review` function?' + review_function = "review" in dir(app) + assert review_function, "Have you created the `review` function?" + execute_sql = "execute_sql:INSERT INTO review (review, rating, title, date, status, employer_id) VALUES (?, ?, ?, ?, ?, ?):[{'id': 'review'}, {'id': 'rating'}, {'id': 'title'}, {'id': 'date'}, {'id': 'status'}, {'id': 'employer_id'}]:commit:True" - assert execute_sql in get_functions(app.review), '`execute_sql` has not been called or has the wrong parameters.' + result = [ + item for item in get_functions(app.review) if item.startswith(execute_sql) + ] + result_len = len(result) == 1 + assert result_len, "`execute_sql` has not been called or has the wrong parameters." + @pytest.mark.test_app_redirect_to_employer_module7 def test_app_redirect_to_employer_module7(): - assert 'review' in dir(app), 'Have you created the `review` function?' - assert 'redirect' in dir(app), '`redirect` has not been imported from flask.' - assert 'url_for' in dir(app), '`url_for` has not been imported from flask.' - assert 'redirect:employer:url_for:employer_id:employer_id' in get_functions(app.review), 'In the `if` are you redirect back to the employer page?' + review_function = "review" in dir(app) + assert review_function, "Have you created the `review` function?" + + redirect_exists = "redirect" in dir(app) + assert redirect_exists, "`redirect` has not been imported from flask." + + url_for_exists = "url_for" in dir(app) + assert url_for_exists, "`url_for` has not been imported from flask." + + redirect_call = "redirect:employer:url_for:employer_id:employer_id" in get_functions( + app.review + ) or "redirect:employer:employer:url_for:employer_id:employer_id" in get_functions( + app.review + ) + assert redirect_call, "In the `if` are you redirecting back to the employer page?" + @pytest.mark.test_employer_review_button_module7 def test_employer_review_button_module7(): - assert template_exists('employer'), 'The `employer.html` template does not exist in the `templates` folder.' - assert 'review:employer_id:employer:id' in template_functions('employer', 'url_for'), 'Does the `Create Review` button have the correct `href`?' + employer_exists = template_exists("employer") + assert ( + employer_exists + ), "The `employer.html` template does not exist in the `templates` folder." + + review_button = "review:employer_id:employer:id" in template_functions( + "employer", "url_for" + ) + assert review_button, "Does the `Create Review` button have the correct `href`?" diff --git a/tests/utils.py b/tests/utils.py index c4193673c..cd0b5209b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,9 +7,11 @@ from bs4 import BeautifulSoup from jinja2 import Environment, PackageLoader, exceptions, meta, nodes -env = Environment(loader=PackageLoader('jobs', 'templates')) -def flatten(d, parent_key='', sep='_'): +env = Environment(loader=PackageLoader("jobs", "templates")) + + +def flatten(d, parent_key="", sep="_"): items = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k @@ -19,18 +21,20 @@ def flatten(d, parent_key='', sep='_'): items.append((new_key, v)) return dict(items) + def get_decorators(source): decorators = {} + def visit_FunctionDef(node): decorators[node.name] = [] for n in node.decorator_list: - name = '' + name = "" if isinstance(n, ast.Call): name = n.func.attr if isinstance(n.func, ast.Attribute) else n.func.id else: name = n.attr if isinstance(n, ast.Attribute) else n.id - args = [a.s for a in n.args] if hasattr(n, 'args') else [] + args = [a.s for a in n.args] if hasattr(n, "args") else [] decorators[node.name].append((name, args)) node_iter = ast.NodeVisitor() @@ -38,16 +42,33 @@ def visit_FunctionDef(node): node_iter.visit(ast.parse(inspect.getsource(source))) return decorators + def get_functions(source): functions = [] def visit_Call(node): path = node.func.attr if isinstance(node.func, ast.Attribute) else node.func.id + if len(node.args) != 0: - path += ':' + ':'.join([str(val) for arg in node.args for val in build_dict(arg).values()]) + + args = [] + for arg in node.args: + arg_dict = build_dict(arg) + arg_dict.pop("value", None) + for val in arg_dict.values(): + args.append(str(val)) + + path += ":" + ":".join(args) if len(node.keywords) != 0: - path += ':' + ':'.join([str(val) for keyword in node.keywords for val in build_dict(keyword).values()]) + + path += ":" + ":".join( + [ + str(val) + for keyword in node.keywords + for val in build_dict(keyword).values() + ] + ) functions.append(path) @@ -56,17 +77,22 @@ def visit_Call(node): node_iter.visit(ast.parse(inspect.getsource(source))) return functions + def get_functions_returns(source): returns = [] def visit_Return(node): - returns.append(build_dict(node)) + return_dict = build_dict(node) + return_dict.pop("value/args/value", None) + return_dict.pop("value/args/args/value", None) + returns.append(return_dict) node_iter = ast.NodeVisitor() node_iter.visit_Return = visit_Return node_iter.visit(ast.parse(inspect.getsource(source))) return returns + def get_statements(source): statements = [] @@ -78,12 +104,22 @@ def visit_If(node): node_iter.visit(ast.parse(inspect.getsource(source))) return statements + def build_dict(node): result = {} - if node.__class__.__name__ == 'Is' or node.__class__.__name__ == 'Eq': - result['node_type'] = node.__class__.__name__ + if node.__class__.__name__ == "Is" or node.__class__.__name__ == "Eq": + result["node_type"] = node.__class__.__name__ for attr in dir(node): - if not attr.startswith("_") and attr != 'ctx' and attr != 'lineno' and attr != 'col_offset': + if ( + not attr.startswith("_") + and attr != "ctx" + and attr != "lineno" + and attr != "end_lineno" + and attr != "col_offset" + and attr != "end_col_offset" + and attr != "kind" + and attr != "n" + ): value = getattr(node, attr) if isinstance(value, ast.AST): value = build_dict(value) @@ -92,42 +128,57 @@ def build_dict(node): value = final[0] if len(final) == 1 else final if value != []: result[attr] = value - return flatten(result, sep='/') + return flatten(result, sep="/") + def list_routes(app): rules = [] for rule in app.url_map.iter_rules(): - methods = ','.join(sorted(rule.methods)) - if rule.endpoint is not 'static': - rules.append(rule.endpoint + ':' + methods + ':' + str(rule)) + methods = ",".join(sorted(rule.methods)) + if rule.endpoint is not "static": + rules.append(rule.endpoint + ":" + methods + ":" + str(rule)) return rules + def template_values(name, function): values = [] 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) + 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 + 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] + 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') + 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 + ':' + args_string += kwargs.key + ":" if isinstance(kwargs.value, nodes.Const): args_string += kwargs.value.value else: @@ -136,88 +187,123 @@ def template_functions(name, function_name): else: args_string += kwargs.value.node.name if isinstance(kwargs.value.arg, nodes.Const): - args_string += ':' + kwargs.value.arg.value + args_string += ":" + kwargs.value.arg.value 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 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') + 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 employer_for(): values = [] - for node in parsed_content('employer').find_all(nodes.For): + for node in parsed_content("employer").find_all(nodes.For): path = node.target.name if isinstance(node.iter, nodes.Name): - path += ':' + node.iter.name + path += ":" + node.iter.name elif isinstance(node.iter, nodes.Call): - path += ':' + node.iter.node.name + ':' + str(node.iter.args[0].value) + ':' + str(node.iter.args[1].node.name) + ':' + str(node.iter.args[1].arg.value) + path += ( + ":" + + node.iter.node.name + + ":" + + str(node.iter.args[0].value) + + ":" + + str(node.iter.args[1].node.name) + + ":" + + str(node.iter.args[1].arg.value) + ) values.append(path) 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) + macros.append(macro.name + ":" + macro.args[0].name) return macros + def template_block(name): blocks = [] for block in parsed_content(name).find_all(nodes.Block): blocks.append(block.name) return blocks + def template_macro_soup(name, macro_name): for macro in parsed_content(name).find_all(nodes.Macro): if macro.name == macro_name: - html = '' + html = "" for template_data in macro.find_all(nodes.TemplateData): html += template_data.data return source_soup(html) + def template_data(name): - html = '' + html = "" for node in parsed_content(name).find_all(nodes.TemplateData): html += node.data return source_soup(html) + def template_variables(name): - return [item.node.name + ':' + item.arg.value for item in parsed_content(name).find_all(nodes.Getitem)] + 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') + return os.path.isfile("jobs/templates/" + name + ".html") + def template_source(name): try: - return env.loader.get_source(env, name + '.html')[0] + return env.loader.get_source(env, name + ".html")[0] except exceptions.TemplateNotFound: return None + def source_soup(source): - return BeautifulSoup(source, 'html.parser') + return BeautifulSoup(source, "html.parser") + def template_soup(name): - return BeautifulSoup(template_source(name), 'html.parser') + return BeautifulSoup(template_source(name), "html.parser") + def template_find(name, tag, limit=None): - return BeautifulSoup(template_source(name), 'html.parser').find_all(tag, limit=limit) + return BeautifulSoup(template_source(name), "html.parser").find_all( + tag, limit=limit + ) + def parsed_content(name): return env.parse(template_source(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) \ No newline at end of file + return ( + node.template.value + + ":" + + ":".join(node.names) + + ":" + + str(node.with_context) + ) From 3f36202f5c63343336c5bf6bd0435e5e715decbc Mon Sep 17 00:00:00 2001 From: Tom Bell Date: Thu, 30 Apr 2020 10:14:44 -0600 Subject: [PATCH 19/20] test: module3 connection fix --- tests/test_module3.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/test_module3.py b/tests/test_module3.py index 80c785c6e..e0cc62461 100644 --- a/tests/test_module3.py +++ b/tests/test_module3.py @@ -29,9 +29,14 @@ def test_app_open_connection_get_attribute_module3(): open_connection = "open_connection" in dir(app) assert open_connection, "Have you defined a function named `open_connection`?" - getattr_g = "getattr:g:_connection:None" in get_functions(app.open_connection) + result = [ + item + for item in get_functions(app.open_connection) + if item.startswith("getattr:g:_connection") + ] + result_len = len(result) == 1 assert ( - getattr_g + result_len ), "Have you used the `getattr` function to get the global `_connection`?" @@ -166,9 +171,14 @@ def test_app_close_connection_module3(): close_connection = "close_connection" in dir(app) assert close_connection, "Have you defined a function named `close_connection`?" - getattr_g = "getattr:g:_connection:None" in get_functions(app.open_connection) + result = [ + item + for item in get_functions(app.open_connection) + if item.startswith("getattr:g:_connection") + ] + result_len = len(result) == 1 assert ( - getattr_g + result_len ), "Have you used the `getattr` function to get the global `_connection`?" close = "close" in get_functions(app.close_connection) From 979931cb2861a34ca922b822aacf99dfa9f73d60 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 23 Feb 2022 22:58:17 +0000 Subject: [PATCH 20/20] fix: Dockerfile to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-ALPINE311-APKTOOLS-1534687 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569447 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-SQLITE-587424 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ac4a657d4..e4a5bece1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.2-alpine +FROM python:3.11.0a5-alpine WORKDIR /src/app/