Reuse log in state for all tests with pytest-playwright and Django - pytest

I'm trying to create a pytest fixture that will allow all my tests to reuse a single authenticated context. The goal is for the fixture to run once, yielding a context. Then the other tests can just reuse the authenticated context via the fixture. I also need this solution to run in CI.
The code below runs, but test_home does not retain a logged in state for some reason and redirects to the log in page.
conftest.py
import logging
import os
import pytest
from django.conf import settings
from user.models import User
from user.tests.factories import UserFactory
#pytest.fixture()
def user() -> User:
return UserFactory()
os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")
settings.ALLOWED_HOSTS += ["127.0.0.1"]
#pytest.mark.django_db
#pytest.fixture()
def playwright_user(user):
password = "HappyAccident$1"
email = "bob.ross#happytrees.com"
user.email = email
user.set_password(password)
user.save()
logging.info("Saved test user")
#pytest.fixture()
def logged_in(
live_server,
playwright,
playwright_user,
) -> None:
"""Create context (i.e. general configuration) for Playwright"""
browser = playwright.chromium.launch(headless=False)
context = browser.new_context(
base_url="http://127.0.0.1:8080", storage_state="auth.json"
)
page = context.new_page()
page.goto("http://127.0.0.1:8080/")
page.get_by_placeholder("E-mail address login").fill("bob.ross#happytrees.com")
page.get_by_placeholder("Password password").fill("HappyAccident$1")
page.get_by_role("button", name="Sign In").click()
storage = context.storage_state(path="auth.json")
logging.info("Created authenticated context")
context.close()
#pytest.fixture(scope="session")
def context(playwright):
browser = playwright.chromium.launch(headless=False)
context = browser.new_context(
base_url="http://127.0.0.1:8080", storage_state="auth.json"
)
yield context
context.close()
test_integration.py (note: the home page below is log in protected.)
import logging
import pytest
from django.urls import reverse
from playwright.sync_api import Page, sync_playwright
pytestmark = pytest.mark.django_db
def test_login(live_server, logged_in):
logging.info("Logged In")
pass
def test_home(
context,
live_server,
page: Page,
) -> None:
"""Create a new Organization Parent Type"""
page.goto(reverse("home"))
# redirects to the log in page instead of taking me to the home page :(
I'm not sure why test_home goes to a log in screen rather than directly opening the home page. If things were working properly it should go directly to the home page. Any help would be really appreciated!

Related

Can we link external API for a confluence page?

I have a requirement where when the user clicks publish button in a Confluence page, need to trigger an external API (post endpoint ) where I can save confluence data in external DB.
Have a look at the Event Listener module and How to build an Event Listener. Basically, you create a plugin that captures the com.atlassian.confluence.event.events.content.page.PageEvent. In your case, you might use PageCreateEvent or PageUpdateEvent. This is for Confluence Server. As for Confluence Cloud, it might be in JavaScript or something.
If you don't feel like developing an addon for it,
then i recommend using an addon adaptavist scriprunner :
that's the easiest way to accomplish that (although not free!)
example from their webpage:
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.jira.JiraApplicationType
import com.atlassian.confluence.event.events.space.SpaceCreateEvent
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder
import
static com.atlassian.sal.api.net.Request.MethodType.POST
def appLinkService = ComponentLocator.getComponent(ApplicationLinkService)
def appLink = appLinkService.getPrimaryApplicationLink(JiraApplicationType)
def applicationLinkRequestFactory = appLink.createAuthenticatedRequestFactory()
def event = event as SpaceCreateEvent
def space = event.space
def input = new JsonBuilder([
projectTypeKey : "business",
projectTemplateKey: "com.atlassian.jira-core-project-templates:jira-core-task-management",
name : space.name,
key : space.key,
lead : event.space.creator.name,
]).toString()
def request = applicationLinkRequestFactory.createRequest(POST, "/rest/api/2/project")
.addHeader("Content-Type", "application/json")
.setEntity(input)
request.execute(new ResponseHandler<Response>() {
#Override
void handle(Response response) throws ResponseException {
if (response.statusCode != 201) {
log.error("Creating jira project failed: ${response.responseBodyAsString}")
}
}
})

new request show up when I do nothing with request monitor

I run the codes from the book and successfully deploy it in my testing "deerdog.me" website as a portal.
There are 3 modules to work: registration, table create and requests monitor.
the 1st user registration module is good.
but when get in with the new registered id/pwd into left-top sign-in form. (under "home" hint, to click for popup)
you see the 2nd table_creating module seems good. create on table by each random number you input.
But the bug is here, here you should not touch with the 3rd module--request, but once you click the "dashboard" in the upper navigation bar.
In new page, weirdly you can see a new request be stealthy added. (theoretically ,the request row can only added by "add-request" function,but not now "get-request" function.
The biggest hint I found is that I only put the "add-request" under the route of #app.route("/newrequest/<tid>") ,but even the route #app.route("/account/createtable"... ,which only has "get-request" function ,can create request in mongodb's db.requests.().
Why does this happen, pls help me check and fix. Thank a lot.
Manipulation logic of 2nd module (creating table and display with the same "/account" route page ,
activate action == you input a random num(2,5 or 66,109) in blank and click "create button" ,then you will see the rows(include a shor url) in same "/account" page.
Manipulation logic of 3rd module (create requests then display in '/dashboard' route page.) should only be activated in such scenario (user copy the short URL in /account route page's table row, then ctrl+ p the short URl to a new browse address ,then under this new "#app.route("/newrequest/") ,a request in mongodb start to legally create" ,now user can jump the manipulation logic of 3rd module. directly create request record without initiating "#app.route("/newrequest/" ,Why does this happen?
#dbhelper.py as below
import pymongo
from bson.objectid import ObjectId
DATABASE = "waitercaller"
class DBHelper:
def __init__(self):
client = pymongo.MongoClient()
self.db = client[DATABASE]
def add_table(self, number, owner):
new_id = self.db.tables.insert({"number": number, "owner": owner})
return new_id
def update_table(self, _id, url):
self.db.tables.update({"_id": _id}, {"$set": {"url": url}})
def get_tables(self, owner_id):
return list(self.db.tables.find({"owner": owner_id}))
def get_table(self, table_id):
return self.db.tables.find_one({"_id": ObjectId(table_id)})
def delete_table(self, table_id):
self.db.tables.remove({"_id": ObjectId(table_id)})
def add_request(self, table_id, time):
table = self.get_table(table_id)
try:
self.db.requests.insert({"owner": table['owner'], "table_number": table[
'number'], "table_id": table_id, "time": time})
return True
except pymongo.errors.DuplicateKeyError:
return False
def get_requests(self, owner_id):
return list(self.db.requests.find({"owner": owner_id}))
def delete_request(self, request_id):
self.db.requests.remove({"_id": ObjectId(request_id)})
main APP code, called waitercaller.py
waitercaller.py
...
#app.route("/")
def home():
#return "Under construction"
#return render_template("home.html")
#registrationform = RegistrationForm()
return render_template("home.html", loginform=LoginForm(), registrationform=RegistrationForm())
#app.route("/dashboard")
#login_required
def dashboard():
now = datetime.datetime.now()
requests = DB.get_requests(current_user.get_id())
for req in requests:
deltaseconds = (now - req['time']).seconds
req['wait_minutes'] = "{}.{}".format((deltaseconds/60), str(deltaseconds % 60).zfill(2))
return render_template("dashboard.html", resolvesubmitform=ResolveForm(),requests=requests)
#app.route("/dashboard/resolve")
#login_required
def dashboard_resolve():
form = ResolveForm(request.form)
request_id = request.args.get("request_id")
#if form.validate():
DB.delete_request(request_id)
return redirect(url_for('dashboard'))
#return render_template("dashboard.html", resolvesubmitform=ResolveForm(),requests=DB.get_requests(request_id))
#app.route("/account")
#login_required
def account():
tables = DB.get_tables(current_user.get_id())
return render_template("account.html", createtableform=CreateTableForm(), tables=tables)
#app.route("/account/createtable", methods=["POST"])
#login_required
def account_createtable():
form = CreateTableForm(request.form)
if form.validate():
tableid = DB.add_table(form.tablenumber.data, current_user.get_id())
new_url = BH.shorten_url(config.base_url + "newrequest/" + str(tableid))
DB.update_table(tableid, new_url)
return redirect(url_for('account'))
return render_template("account.html", createtableform=form, tables=DB.get_tables(current_user.get_id()))
#app.route("/account/deletetable")
#login_required
def account_deletetable():
tableid = request.args.get("tableid")
DB.delete_table(tableid)
return redirect(url_for('account'))
#app.route("/newrequest/<tid>")
def new_request(tid):
DB.add_request(tid, datetime.datetime.now())
return "Your request has been logged and a waiter will be with you shortly"
if __name__ == '__main__':
app.run(port=5000, debug=True)

Jenkins: Active Choices Parameter + Groovy to build a list based on REST responde

I have a REST client that returns me a list of systems.
I need this list to be as a parameter for a jenkins job.
I think I need Actice Choices Parameter plugin with Groovy and HTTPBuilder in order to do that.
What do you guys think?
I did not find a way to install HTTPBuilder into Jenkins.
Is there any other way that you guys think it is possible?
I have run into the same problem trying to parse parameters via groovy script. Arun's answer did not work for me. However, I have managed to get it to work using the following:
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.URL
import java.net.URLConnection
import groovy.json.JsonSlurper
def choices = []
def url = new URL("some.data.url")
def conn = url.openConnection()
conn.setDoOutput(true)
def reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))
def results = new JsonSlurper().parseText(reader.getText());
reader.close()
results.each { data -> choices.push(data.field) }
return choices.sort()
First paste the JSON body snapshot output -or whatever your REST client is going to return. That'll help.
For ex: if it'll return a JSON object then you can use Active Choice Parameter's Groovy script step - OR Scriptler script (within the Active Choice Parameter plugin). PS: Scriptler script runs in the same JVM of Jenkins process so it has access to Jenkins/etc object for free. You don't need HTTPBuilder or anything. See the code sample below.
Assuming if your REST client is returning a JSON object and from that object if you want to list hostname of the system or some field name then replace the following variable with that and you'll get it listed while doing "Build with parameters" from Jenkins job's dashboard.
import groovy.json.JsonSlurper
//this will be your URL which will return something, tweak it if you want to pass parameters or username/password acc.
def SOME_URL = "https://koba.baby.com/some_url"
// now connect to the URL and create a connection variable 'conn'
def conn = SOME_URL.toURL().openConnection()
// create a list variable 'servernames'
def servernames = []
// if connection response was successful i.e. http protocol return code was 200, then do the following
if( conn.responseCode == 200 ) {
// get the results / output of the URL connection in a variable 'results'
def results = new JsonSlurper().parseText(conn.content.text)
// to see results variable output uncomment the next line
//println results
// now read each element in the 'results' variable and pick servername/somefield variable into the list variable 'servernames'
results.each { id, data -> servernames.push(data.someField_or_HostName) }
}
return servernames.sort().unique()
// return servernames.sort()

Pipeline workflow and variables

I have Facebook authentication working on my site, but I need the user to fill a profile form during his authentication. I have used an authentication pipeline to do so but whithout success. The pipeline is being called like it should, but the result is an error.
Let's say I need his mobile number - consider it does not come from Facebook.
Please consider:
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
mobile = models.IntegerField()
settings.py
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.mail.mail_validation',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details',
'myapp.pipeline.fill_profile',
)
pipeline.py
from myapp.models import Profile
from social.pipeline.partial import partial
#partial
def fill_profile(strategy, details, user=None, is_new=False, *args, **kwargs):
try:
if user and user.profile:
return
except:
return redirect('myapp.views.profile')
myapp/views.py
from django.shortcuts import render, redirect
from myapp.models import Perfil
def profile(request):
if request.method == 'POST':
profile = Perfil(user=request.user,mobile=request.POST.get('mobile'))
profile.save()
backend = request.session['partial_pipeline']['backend']
redirect('social:complete', backend=)
return render(request,'profile.html')
The profile.html is just a form with an input text box named 'mobile' and a submit button.
Then I get this error:
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x03C2FB10>>": "Profile.user" must be a "User" instance.
Why can't I access the User instance since the user in auth_user table is already there (I suppose)?
Please, what's wrong with this?
You can't access the user in request.user because it's not logged in yet, the user will be logged in social complete view after the pipeline executed. Usually partial pipeline views will save the form data into the session and then the pipeline will pick it and save it. Also you can set the user id in the session in your pipeline and then pick that value in your view. For example:
#partial
def fill_profile(strategy, user, *args, **kwargs):
...
strategy.session_set('user_id', user.id)
return redirect(...)

Scala-Lift redirect user after login

I want to redirect to a certain page after a user logs in to my Scala Lift web application. I found this answer which doesn't seem to work:
In my User object (which is a MetaMegaProtoUser) I override the following method like so:
object User extends User with MetaMegaProtoUser[User] {
override def loginFirst = If(
loggedIn_? _,
() => {
import net.liftweb.http.{RedirectWithState, RedirectState}
val uri = Full("/myPicks")
println("login sucessful .. redirecting!..")
RedirectWithState(
loginPageURL,
RedirectState( ()=>{loginRedirect.set(uri)})
)
}
)
}
This doesn’t work. Any ideas?
loginFirst defines a LocParam which can be used for specifying where to send the user if they are not logged in. It is used in conjunction with SiteMap.
For the page you want to protect, you can modify that entry like:
Menu("Protected Page") / "protected" >> User.loginFirst
That should test whether the user is logged in when you access /protected and, if they are not, set the loginRedirect SessionVar and display the login form. On a successful login, you should be redirected to the page specified in loginRedirect.
I believe you can also just use: override def homePage = "/myPicks" if you want to set a default page to redirect to.