Home > Blockchain >  Fetching data to Django view working manually but not with Selenium. Why?
Fetching data to Django view working manually but not with Selenium. Why?

Time:02-15

I am using Selenium to test a Django app I have created.
The following code works fine 'manually' but does not work with Selenium.
Why ?


## dummy.html ##
{% extends 'main.html' %}
{% load static %}

{% block title %}
Dummy page
{% endblock title %}

{% block meta %}
<meta name="robots" content="noindex">
{% endblock meta %}

{% block content %}

<div >
    <div >
        <div >
            <h1 >Dummy page</h1>
        </div>
    </div>
    <br>
    <div >
        <div >
            <h2 >THIS IS A DUMMY PAGE</h2>
        </div>
    </div>
    <br>
    <div >
        <button id="trigger-dummy-button" type="button"
            
            title="Trigger dummy button">Trigger dummy button</button>
    </div>
</div>

<script> 
    var someDummyURL = "{% url 'home_page:another_dummy_view' %}";
</script>

<script type="module" src="{% static '/js/dummy.js' %}">  </script>

{% endblock content %}


## dummy.js ##
'use strict';

var newValue;

async function postDummyFunction(request) {
    var response = await fetch(request);
    if (response.ok) {
        var updatedReturnedValue = await response.json();
        newValue = updatedReturnedValue.returnedValue;
        alert('-- postDummyFunction --  |  newValue = '   newValue);
        // When the script is triggered manually by clicking, response.ok = true and newValue = "Hello World OK !"
        return newValue;
    } else {
        // When the script is triggered by Selenium, response.ok = false
        alert('-- postDummyFunction --  |  response.ok ? '   response.ok);
        return false;
    }
};


document.addEventListener("DOMContentLoaded", function () {

    var triggerDummyButton = document.getElementById("trigger-dummy-button");
    var someDummyVariable = "Hello World ";
    var toPostURL = someDummyURL;
    
    triggerDummyButton.onclick = (e) => {
        var data = {"dummyValue": someDummyVariable};
        var request = new Request(toPostURL, {
            method: "POST",
            mode: "same-origin",
            headers: {
                "Content-Type": "application/json",
                'X-Requested-With':'XMLHttpRequest',
                "Accept": "application/json",
                'X-CSRFToken': csrftoken
            },
            body: JSON.stringify(data),
        });
        // Works with Selenium
        // if alert(data.dummyValue), browser correctly displays "Hello World "
        postDummyFunction(request);
    }
});


## views.py ##
import json

from django.shortcuts import render, redirect
from django.http import JsonResponse


def dev_dummy_script(request):
    context = {}
    return render(request, "dummy.html", context)


def another_dummy_view(request):
    if request.method == "POST":
        post_data = json.loads(request.body.decode("utf-8"))
        posted_value = post_data["dummyValue"]
        value_to_return = posted_value   " "   "OK !"
        response = JsonResponse({"returnedValue": value_to_return})
        return response


## test_selenium.py ##

import time
import os
from decimal import Decimal
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from django.contrib.staticfiles.testing import StaticLiveServerTestCase

from core.settings import BASE_DIR

PATH_TO_WEBDRIVER_CHROME = os.path.join(
    BASE_DIR, "test", "webdrivers", "chromedriver.exe"
)

PATH_TO_WEBDRIVER_FIREFOX = os.path.join(BASE_DIR, "test", "webdrivers", "geckodriver")

SHORT_TIME = 2.5
LONG_TIME = 6
DEFAULT_WAIT_TIME = 10


class FooTest(StaticLiveServerTestCase):
    def setUp(self):
        self.browser = webdriver.Firefox(executable_path=PATH_TO_WEBDRIVER_FIREFOX)

    def tearDown(self):
        self.browser.close()

    def testfoo(self):

        self.browser.get(("%s%s" % (self.live_server_url, "/dev/dummy_script/")))

        elem_trigger_btn = WebDriverWait(self.browser, DEFAULT_WAIT_TIME).until(
            EC.element_to_be_clickable((By.ID, "trigger-dummy-button"))
        )

        elem_trigger_btn.click()

        time.sleep(LONG_TIME)

        self.browser.close()


versions

  • Django==3.2
  • selenium==3.141.0
  • Firefox 96.0.3

If I trigger python manage.py runserver, manually head to dummy_script and click on the button, it works fine, i.e. alert window pops up and displays "Hello World OK !".

If I trigger the script with pytest and selenium, the code is executed, but response returned from fetch is ko.

EDIT

main.html

<!DOCTYPE html>
{% load static %}
<html lang="fr">

<head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link href="{% static 'css/main.css' %}" rel="stylesheet">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">

    <script src="{% static 'js/bootstrap.js' %}"></script>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds 3Ulb9Bn9Plx0x4"
        crossorigin="anonymous"></script>

    <script type="text/javascript">

        function getCookie(name) {
            let cookieValue = null;
            if (document.cookie && document.cookie !== '') {
                const cookies = document.cookie.split(';');
                for (let i = 0; i < cookies.length; i  ) {
                    const cookie = cookies[i].trim();
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length   1) === (name   '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length   1));
                        break;
                    }
                }
            }
            return cookieValue;
        }
        const csrftoken = getCookie('csrftoken');

    </script>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"
        integrity="sha256-/xUj 3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>

    <title>
        {% block title %}
        {% endblock title %}
    </title>
    {% block meta %}
    {% endblock meta %}

</head>

<body >
    <section  id="page-content">
        {% block content %}
        {% endblock content %}
    </section>

    <script type="module" src="{% static '/js/main.js' %}">  </script>

</body>

</html>

CodePudding user response:

I realise that when I use Selenium, the function getCookie return csrftoken = null. As a result, It is impossible to fetch data to django view.

To be able to make it work with Selenium in test mode only, I have added the following conditional decorator before 'another_dummy_view'.

views.py


def conditional_decorator(dec, condition):
    def decorator(func):
        if not condition:
            # Return the function unchanged, not decorated.
            return func
        return dec(func)
    return decorator


def dev_dummy_script(request):
    context = {}
    return render(request, "dummy_2.html", context)


@conditional_decorator(csrf_exempt, DEBUG)
def another_dummy_view(request):
    if request.method == "POST":
        post_data = json.loads(request.body.decode("utf-8"))
        posted_value = post_data["dummyValue"]
        value_to_return = posted_value   " "   "OK !"
        response = JsonResponse({"returnedValue": value_to_return})
        return response

  • Related