Repository: django-cms/django-cms (language: python)

Issue:
[DOC] Notes and minor errors in the doc
<!--
Please fill in each section below, otherwise, your issue will be closed.
This info allows django CMS maintainers to diagnose (and fix!) your issue
as quickly as possible.
-->

## Description
1) On this [page](https://docs.django-cms.org/en/latest/contributing/code.html),  
>```
> *************
> In a nutshell
> *************
> 
> Here's what the contribution process looks like in brief:
> 
> #. Fork our `GitHub`_ repository, https://github.com/django-cms/django-cms
> #. Work locally and push your changes to your repository.
> ```
I thought "Work locally and push your changes to your repository" is quite ambiguous,
especially for a newbie. One idea that comes to mind would be to link to a section that
talks about how to setup djangoCMS from git source. Perhaps there is a link to it already too

I noticed these minor doc errors while trying to install django-cms.

2) I think [here](https://docs.django-cms.org/en/latest/introduction/01-install.html#install-django-cms-by-hand:~:text=The%20last%20option%20is%20to%20install%20is%20creating%20a%20django%20CMS%20project%20manually%20into%20a%20local%20virtual%20environment%20by%20running%20the%20djangocms%20command) could be reworded but the issue can be closed if it's too minor. It could be reworded to
`The last option to install is creating a django CMS project
manually into a local virtual environment by running the 
djangocms command` (removing the first `is`, so it reads better.)

3) On the same page, under `Installing Django CMS by hand` section.
```
.. note::

    You can also use this guide to help you install django CMS as part of an existing
    project. However, the guide assumes that you are starting with a blank project, so
    you will need to adapt the steps below appropriately as required. You
```
I think the "You" that ends the paragraph can be removed, unless the writer is continuing a thought.


## Do you want to help fix this documentation issue?
* [x] Yes, I want to help fix this issue and I will join the channel #documentation on our [Discord Server](https://discord-docs-channel.django-cms.org) to confirm with the team that a PR is welcome.
* [ ] No, I only want to report the issue.



Discussion / hints:
@mrbazzan Great observations! Would you like to come up with a PR?
Yes, I would.
I found [this](https://github.com/django-cms/django-cms/blob/3f395b52ec9b172e3960a86a7e2c4d1dfe62f8cc/docs/how_to/contributing.rst) which has since been removed...

Fix patch:
diff --git a/docs/contributing/code.rst b/docs/contributing/code.rst
index 2cfd57e7d66..ae4c04d4e88 100644
--- a/docs/contributing/code.rst
+++ b/docs/contributing/code.rst
@@ -20,7 +20,7 @@ Here's what the contribution process looks like in brief:
 #. After that, please join our Discord server (`#contributors <https://discord-contributors-channel.django-cms.org>`_). This group of friendly community members is dedicated to reviewing pull requests. Report your PR and find a “pr review buddy” who is going to review your pull request.
 #. Get acknowledged by the django CMS community for your contribution
 
-See the :ref:`contributing_patch` how-to document for a walk-through of this process.
+See :ref:`contributing_patch` for a walk-through of this process.
 
 
 ********************************
@@ -202,3 +202,8 @@ the source modules.
 .. _freenode: http://freenode.net/
 .. _pull request: http://help.github.com/send-pull-requests/
 .. _git: http://git-scm.com/
+
+.. toctree::
+    :maxdepth: 1
+
+    contribute-patch
diff --git a/docs/contributing/contribute-patch.rst b/docs/contributing/contribute-patch.rst
new file mode 100644
index 00000000000..a94c1c489fa
--- /dev/null
+++ b/docs/contributing/contribute-patch.rst
@@ -0,0 +1,188 @@
+..  _contributing_patch:
+
+####################
+Contributing a patch
+####################
+
+.. _start-contributing:
+
+**********
+The basics
+**********
+
+The basic workflow for a code contribution will typically run as follows:
+
+#. Fork the `django CMS project <https://github.com/django-cms/django-cms>`_ GitHub repository to your
+   own GitHub account
+#. Clone your fork locally::
+
+    git clone git@github.com:YOUR_USERNAME/django-cms.git
+
+#. Create a virtualenv::
+
+    cd django-cms
+    python3 -m venv .venv
+    source .venv/bin/activate
+
+#. Install its dependencies::
+
+    pip install -r test_requirements/django-X.Y.txt
+
+   Replace ``X.Y`` with whichever version of Django you want to work with.
+   Check the supported versions in the "test_requirements/" directory
+
+#. Create a new branch for your work::
+
+    git checkout -b my_fix
+
+#. Edit the django CMS codebase to implement the fix or feature.
+#. Run the test suite::
+
+    python manage.py test
+
+#. Commit and push your code::
+
+    git commit
+    git push origin my_fix
+
+#. Open a pull request on GitHub.
+
+.. _test-writing:
+
+*******************
+How to write a test
+*******************
+
+The django CMS test suite contains a mix of unit tests, functional tests, regression tests and
+integration tests.
+
+Depending on your contribution, you will write a mix of them.
+
+Let's start with something simple. We'll assume you have set up your environment correctly as
+:ref:`described above <start-contributing>`.
+
+Let's say you want to test the behaviour of the ``CMSPluginBase.render`` method:
+
+.. code-block:: python
+
+    class CMSPluginBase(admin.ModelAdmin, metaclass=CMSPluginBaseMetaclass):
+
+        ...
+
+        def render(self, context, instance, placeholder):
+            context['instance'] = instance
+            context['placeholder'] = placeholder
+            return context
+
+Writing a unit test for it will require us to test whether the returned ``context`` object contains
+the declared attributes with the correct values.
+
+We will start with a new class in an existing django CMS test module (``cms.tests.test_plugins`` in
+this case):
+
+.. code-block:: python
+
+    class SimplePluginTestCase(CMSTestCase):
+        pass
+
+Let's try to run it:
+
+.. code-block:: bash
+
+    python manage.py test cms.tests.test_plugins.SimplePluginTestCase
+
+
+This will call the new test case class only and it's handy when creating new tests and iterating
+quickly through the steps. A full test run (``python manage.py test``) is required before opening
+a pull request.
+
+This is the output you'll get::
+
+    Found 0 test(s).
+    System check identified no issues (0 silenced).
+
+    ----------------------------------------------------------------------
+    Ran 0 tests in 0.000s
+
+    NO TESTS RAN
+
+Which is correct as we have no test in our test case. Let's add an empty one:
+
+.. code-block:: python
+
+    class SimplePluginTestCase(CMSTestCase):
+
+        def test_render_method(self):
+            pass
+
+Running the test command again will return a slightly different output::
+
+    Found 1 test(s).
+    Creating test database for alias 'default'...
+    System check identified no issues (0 silenced).
+    .
+    ----------------------------------------------------------------------
+    Ran 1 test in 0.001s
+
+    OK
+    Destroying test database for alias 'default'...
+
+This looks better, but it's not that meaningful as we're not testing anything.
+
+Write a real test:
+
+.. code-block:: python
+
+    class SimplePluginTestCase(CMSTestCase):
+
+        def test_render_method(self):
+            """
+            Tests the CMSPluginBase.render method by checking that the appropriate variables
+            are set in the returned context
+            """
+            from cms.api import create_page
+            my_page = create_page('home', language='en', template='col_two.html')
+            placeholder = my_page.get_placeholders(language='en')
+            context = self.get_context('/', page=my_page)
+            plugin = CMSPluginBase()
+
+            new_context = plugin.render(context, None, placeholder)
+            self.assertTrue('placeholder' in new_context)
+            self.assertEqual(placeholder, context['placeholder'])
+            self.assertTrue('instance' in new_context)
+            self.assertIsNone(new_context['instance'])
+
+and run it::
+
+    Found 1 test(s).
+    Creating test database for alias 'default'...
+    System check identified no issues (0 silenced).
+    .
+    ----------------------------------------------------------------------
+    Ran 1 test in 0.018s
+
+    OK
+    Destroying test database for alias 'default'...
+
+The output is quite similar to the previous run, but the longer execution time gives us a hint that
+this test is actually doing something.
+
+Let's quickly check the test code.
+
+To test ``CMSPluginBase.render`` method we need a RequestContext instance and a placeholder. As
+``CMSPluginBase`` does not have any :ref:`configuration model <storing configuration>`,
+the instance argument can be ``None``.
+
+#. Create a page instance to get the placeholder
+#. Get the placeholder by filtering the placeholders of the page instance on the language
+#. Create a context instance by using the provided super class method
+#. Call the render method on a ``CMSPluginBase`` instance; being stateless, it's easy to call
+   ``render`` of a bare instance of the ``CMSPluginBase`` class, which helps in tests
+#. Assert a few things the method must provide on the returned context instance
+
+As you see, even a simple test like this assumes and uses many feature of the test utilities
+provided by django CMS. Before attempting to write a test, take your time to explore the content of
+``cms.test_utils`` package and check the shipped templates, example applications and, most of all,
+the base ``testcases`` defined in ``cms.test_utils.testscases`` which provide *a lot* of useful
+methods to prepare the environment for our tests or to create useful test data.
+
diff --git a/docs/introduction/01-install.rst b/docs/introduction/01-install.rst
index 27ab9a65dda..d81235bd8b9 100644
--- a/docs/introduction/01-install.rst
+++ b/docs/introduction/01-install.rst
@@ -27,7 +27,7 @@ ways to install django CMS for free.
    <https://www.django-cms.org/en/blog/2021/01/19/how-you-spin-up-a-django-cms-project-in-less-than-5-minutes/>`_.
    It is a good way for a developer locally without an external vendor and we use this
    option in this django CMS demo.
-3. The last option is to install is creating a django CMS project manually into a local
+3. The last option to install is creating a django CMS project manually into a local
    virtual environment by running the ``djangocms`` command. This will create a new
    Django project set up for running django CMS based on a django CMS-specific project
    template. See :ref:`Installing django CMS by hand <install-django-cms-by-hand>` for
@@ -147,7 +147,7 @@ how django CMS is set up like a typical Django project.
 
     You can also use this guide to help you install django CMS as part of an existing
     project. However, the guide assumes that you are starting with a blank project, so
-    you will need to adapt the steps below appropriately as required. You
+    you will need to adapt the steps below appropriately as required.
 
 This document assumes you have some basic familiarity with Python and Django. After
 you've integrated django CMS into your project, you should be able to follow the


Test patch:
diff --git a/docs/contributing/testing.rst b/docs/contributing/testing.rst
index c8786924c69..1d6d654a6bb 100644
--- a/docs/contributing/testing.rst
+++ b/docs/contributing/testing.rst
@@ -179,6 +179,8 @@ everybody's future work much easier by doing so). We'll always accept contributi
 test without code, but not code without a test - which should give you an idea of how
 important tests are.
 
+:ref:`See how to write a test patch <test-writing>`.
+
 What we need
 ~~~~~~~~~~~~