Vue.js unit test w avoriaz , how to test submit event - unit-testing

I am trying to test a form submit.. it seems that trigger is not appropriate
1) calls store action login when the form is submitted
LoginPage.vue
TypeError: wrapper.find(...).trigger is not a function
at Context.<anonymous> (webpack:///test/unit/specs/pages/LoginPage.spec.js:35:28 <- index.js:50826:29)
my vue component to be tested
LoginPage.vue
<template>
<div class="container">
<div class="login-page">
<h1 class="title">Login to existing account</h1>
<form #submit.prevent="submit()" class="form form--login grid">
<div class="row">
<label for="login__email">Email</label>
<input type="text" id="login__email" class="input--block" v-model="email" v-on:keyup="clearErrorMessage" />
</div>
<div class="row">
<label for="login__password">Password</label>
<input type="password" id="login__password" class="input--block" v-model="password" v-on:keyup="clearErrorMessage" />
</div><!-- /.row -->
<div class="row">
<label></label>
<button id="submit" type="submit">Login</button>
</div><!-- /.row -->
<div v-show='hasError' class="row">
<label></label>
<p class="error">Invalid credentials</p>
</div>
</form>
</div><!-- /.login-page -->
</div>
</template>
<script>
import store from '#/vuex/store'
import { mapActions } from 'vuex'
import _ from 'underscore'
export default {
name: 'loginPage',
data () {
return {
email: 'john.doe#domain.com',
password: 'john123',
hasError: false
}
},
methods: _.extend({}, mapActions(['login']), {
clearErrorMessage () {
this.hasError = false
},
submit () {
return this.login({user: { email: this.email, password: this.password }})
.then((logged) => {
if (logged) {
this.$router.push('shoppinglists')
} else {
this.hasError = true
}
})
}
}),
store
}
</script>
LoginPage.spec.js
import LoginPage from '#/pages/LoginPage'
import Vue from 'vue'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('LoginPage.vue', () => {
let actions
let getters
let store
beforeEach(() => {
actions = {
login: sinon.stub()
}
getters = {
isAuthenticated: () => {
state => state.isAuthenticated
}
}
store = new Vuex.Store({
actions,
getters,
state: {
isAuthenticated: false,
currentUserId: ''
}
})
})
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
wrapper.find('#submit').trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})
})

Should be trigger 'click' on the form tag !
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
const form = wrapper.find('form')[0]
form.trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})

Related

How to make when a user clicks accept all cookies google analytics is enabled , but when user clicks only necessary google analytics is disabled

First thank you for reading my question and trying to help.
I'm trying to make a cookie accept footer and when user clicks on accept all I want to enable google analytics and google tag manager. But when user clicks on only necessary I only want google tag manager and I don't know how to make that.
This is my cookie footer :
import { useState } from "react";
import Cookies from 'js-cookie'
function FooterCookies() {
cosnt [ show, setShow ] = useState(true)
const content = {
text : 'This site uses services that uses cookies to deliver better experience and analyze traffic. You can learn more about the services we use at our ',
policy : 'Privacy Policy.'
}
const setCookies = () => {
Cookies.set('cookie', 'true', { expires: 2 })
}
return (
<>
{show ?
(
<div
className=" h-auto bg-black/30 fixed
bottom-0 text-cus-yellow rounded
py-4 px-8 flex justify-evenly items-center z-[100]
max75:flex-col max75:px-5 max75:py-3 max30:py-1 max30:px-1.5 max75:items-start">
<div className=" w-85% max70:text-sm flex items-center max75:w-100% max75:items-start max75:mb-3 " >
<p className=" text-[13px] pt-1">
{content.text}
<span className=" text-[16px] underline cursor-pointer ">
{content.policy}
</span>
</p>
</div>
<div className=" flex " >
<div onClick={() => setCookies()}>
<div onClick={() => setShow(false)}
className="border border-cus-yellow text-cus-yellow hover:bg-cus-yellow/60
transition-all hover:text-black w-28 h-80% bg-black/40 cursor-pointer
flex flex-col justify-center items-center whitespace-nowrap
duration-500 text-lg max75:text-base font-medium mx-2 px-1 py-2 max75:mx-0 max75:py-1 max75:px-0.5 "
>Accept All</div>
</div>
<div onClick={() => setShow(false)}
className="border border-cus-yellow text-cus-yellow hover:bg-cus-yellow/60
transition-all hover:text-black w-36 h-80% bg-black/40 cursor-pointer
flex flex-col justify-center items-center whitespace-nowrap
duration-500 text-lg max75:text-base font-medium mx-2 px-1 py-[13.76px] max75:mx-0 max75:ml-3 max75:px-0.5 max75:py-[8.75px] "
>Only Nessesary</div>
</div>
</div>
)
:
<></>
}
</>
)
}
This is my gtag.js :
export const GA_TRACKING_ID = 'XXXXXXXXX'
export const pageview = (url) => {
window.gtag('config', GA_TRACKING_ID, {
page_path: url,
})
}
export const event = ({ action, category, label, value }) => {
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value,
})
}
This is myApp.js :
import '../styles/globals.css'
import 'tailwindcss/tailwind.css'
import { useEffect } from 'react'
import Script from 'next/script'
import { useRouter } from 'next/router'
import * as gtag from '../lib/gtag'
function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url) => {
gtag.pageview(url)
}
router.events.on('routeChangeComplete', handleRouteChange)
router.events.on('hashChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
router.events.off('hashChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
/>
<Script
id="gtag-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gtag.GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
<Script id="google-tag-manager" strategy="afterInteractive">
{`
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXXXXXX');
`}
</Script>
<Component {...pageProps} />
</>
)
}
export default MyApp
This is my document.js :
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
import { GA_TRACKING_ID } from '../lib/gtag'
export default function Document() {
return (
<Html>
<Head>
<Script
id='google tags manager'
dangerouslySetInnerHTML={{
__html:`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXXXXXX');`,
}}
/>
<Script
async src={`https://www.googlemanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
</Head>
<body>
<noscript
dangerouslySetInnerHTML={{
__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden" />`,
}}
/>
<Main />
<NextScript />
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
`,
}}
/>
</body>
</Html>
)
}

braintree hosted payment fields client undefined Ember 3.25

Ember and Braintree Hosted Fields are not a good mix so far, Braintree Support are out of ideas on this one. When the form renders on the page it calls the action to create the client. The client is undefined.
picture-this-44ac48bef9f8df633632a4d202da2379.js:57 Uncaught TypeError: Cannot read property 'client' of undefined
component hbs
<script src="https://js.braintreegateway.com/web/3.81.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.81.0/js/hosted-fields.min.js"></script>
<div class="demo-frame" {{did-insert this.setupBraintreeHostedFields}}>
<form action="/" method="post" id="cardForm" >
<label class="hosted-fields--label" for="card-number">Card Number</label>
<div id="card-number" class="hosted-field"></div>
<label class="hosted-fields--label" for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="hosted-field"></div>
<label class="hosted-fields--label" for="cvv">CVV</label>
<div id="cvv" class="hosted-field"></div>
<label class="hosted-fields--label" for="postal-code">Postal Code</label>
<div id="postal-code" class="hosted-field"></div>
<div class="button-container">
<input type="submit" class="button button--small button--green" value="Purchase" id="submit"/>
</div>
</form>
</div>
component class
import Component from '#glimmer/component';
import { action } from '#ember/object';
import { inject as service } from '#ember/service';
import { tracked } from '#glimmer/tracking';
import { braintree } from 'braintree-web';
export default class CardPaymentComponent extends Component {
#action
setupBraintreeHostedFields() {
alert('booh');
var form = document.querySelector('#cardForm');
var authorization = 'sandbox_24nzd6x7_gyvpsk2myght4c2p';
braintree.client.create({
authorization: authorization
}, function(err, clientInstance) {
if (err) {
console.error(err);
return;
}
createHostedFields(clientInstance);
});
function createHostedFields(clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '16px',
'font-family': 'courier, monospace',
'font-weight': 'lighter',
'color': '#ccc'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': '#8bdda8'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YYYY'
},
postalCode: {
selector: '#postal-code',
placeholder: '11111'
}
}
}, function (err, hostedFieldsInstance) {
var tokenize = function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
alert('Something went wrong. Check your card details and try again.');
return;
}
alert('Submit your nonce (' + payload.nonce + ') to your server here!');
});
};
form.addEventListener('submit', tokenize, false);
});
}
}
}
package.json
...
"ember-cli": "^3.25.2",
"braintree-web": "^3.81.0",
...
** Final Solution **
NPM braintree-web not required. Component class does not have access to the Braintree Window object. Move the tags to the app/index.html as outlined in the accepted answer.
component hbs
<article class="rental">
<form action="/" method="post" id="cardForm">
<label class="hosted-fields--label" for="card-number">Cardholder Name</label>
<div id="card-holder-name" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="card-number">Email</label>
<div id="email" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="card-number">Card Number</label>
<div id="card-number" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="cvv">CVV</label>
<div id="cvv" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="postal-code">Postal Code</label>
<div id="postal-code" class="hosted-field payment"></div>
<div class="button-container">
<input type="submit" class="button" value="Purchase" id="submit"/>
</div>
</form>
</article>
<script>
var form = document.querySelector('#cardForm');
var authorization = 'sandbox_24nzd6x7_gyvpsk2myght4c2p';
braintree.client.create({
authorization: authorization
}, function(err, clientInstance) {
if (err) {
console.error(err);
return;
}
createHostedFields(clientInstance);
});
function createHostedFields(clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '1.2em',
'font-family': 'courier, monospace',
'font-weight': 'lighter',
'color': '#ccc'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': '#8bdda8'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YYYY'
},
postalCode: {
selector: '#postal-code',
placeholder: '11111'
}
}
}, function (err, hostedFieldsInstance) {
var tokenize = function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
alert('Something went wrong. Check your card details and try again.');
return;
}
alert('Submit your nonce (' + payload.nonce + ') to your server here!');
});
};
form.addEventListener('submit', tokenize, false);
});
}
</script>
You can use Braintree SDK via either the direct script tag or using the npm module with the help of ember-auto-import. In your case, you are using both.
For simplicity, let's use the script tag to inject the SDK. The issue in your snippet is that you are trying to load the script tag inside a component handlebar file. the handlebars (.hbs file) cannot load scripts using a <script> tag. We need to move the script tag to the index.html file present inside the app folder. This will load the SDK properly to be used inside a component.
app/index.html:
<body>
...
<script src="https://js.braintreegateway.com/web/3.81.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.81.0/js/hosted-fields.min.js"></script>
{{content-for "body-footer"}}
</body>
Once you inject the SDK properly, you can use the braintree window object without any issue.

Vue.js instant search from API REST Framework using axios

I have a problem. I want to create instant search, without any search button, that when i'm typing e.g. more than 3 letters, my results will be instant show below.
My code:
<template>
<div class="nav-scroller py-1 mb-2">
<div class="nav d-flex justify-content-between">
<input v-model="keyword" class="form-control" type="text" placeholder="Search" aria-label="Search">
<div v-bind:key="result.id" v-for="result in results">
<p>Results are: {{ result.title }}</p>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Home',
components: {
},
data() {
return {
keyword: '',
results: [],
}
},
methods: {
getResults() {
axios.get("http://127.0.0.1:8000/api/v1/books/?search="+this.keyword)
.then(res => (this.results = res.data))
.catch(err => console.log(err));
}
},
created() {
this.getResults()
}
}
</script>
Now my 'keyword' parameter is probably not passed to the url, because when I refresh the page, all records from APi are the results.
Could you help me?
You should either call method when input changes
<input v-model="keyword" #input="getResults">
and method:
getResults() {
if (this.keyword.length > 3)
axios.get("http://127.0.0.1:8000/api/v1/books/?search="+this.keyword)
.then(res => (this.results = res.data))
.catch(err => console.log(err));
}
}
Or watcher can be used. When keyword changes watcher will call getResults method.
watch: {
keyword: "getResults"
}
Use watcher for the keyword value update.
Whenever keyword is more than 3 letters, request the getResults() method to search.
export default {
name: 'Home',
components: {
},
data() {
return {
keyword: '',
results: [],
}
},
watch: {
keyword: function(newVal) {
if (newVal.length >2) {
this.getResults();
}
}
},
methods: {
getResults() {
axios.get("http://127.0.0.1:8000/api/v1/books/?search="+this.keyword)
.then(res => (this.results = res.data))
.catch(err => console.log(err));
}
},
created() {
this.getResults()
}
}

Vue.js - Vuetify - Hard time to test a menu component

Given the following Toolbar component, I am trying to test the locale changing upon click on menu item ... but the wrapper content is not changing after a mouseover event on the menu selector ...
Toolbar.vue
<template>
<v-toolbar height="80px" fixed>
<v-toolbar-title>
<img src="#/assets/images/app_logo.png" alt="">
<v-menu bottom offset-y open-on-hover class="btn btn--flat" style="margin-bottom: 12px;">
<v-btn id="current-flag" flat slot="activator">
<img :src="flagImage(currentLocaleIndex)" width="24px">
</v-btn>
<v-list>
<v-list-tile v-for="(locale, index) in locales" :key="index" #click="switchLocale(index)">
<div class="list__tile__avatar avatar--tile" #click="switchLocale(index)">
<img :src="flagImage(index)" width="24px">
</div>
<div class="list__tile__title" v-html="flagTitle(index)"></div>
</v-list-tile>
</v-list>
</v-menu>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<!-- MENU HOME-->
<v-btn flat #click="$router.push(menuItems.home.link)">
<v-icon left>{{ menuItems.home.icon }}</v-icon>
<span>{{ menuItems.home.title | translate }}</span>
</v-btn>
<!-- ABOUT HOME-->
<v-btn flat #click="$router.push(menuItems.about.link)">
<v-icon left>{{ menuItems.about.icon }}</v-icon>
<span>{{ menuItems.about.title | translate }}</span>
</v-btn>
</v-toolbar-items>
</v-toolbar>
</template>
<script>
// import i18n from '#/locales'
export default {
name: "Toolbar",
props: ["appName"],
data() {
return {
menuItems: {
home: { icon: "home", title: "Home", link: "/" },
about: { icon: "info", title: "About", link: "/about" }
},
locales: [
{ id: "en", title: "English", flag: "#/assets/images/flag_en_24.png" },
{ id: "fr", title: "Français", flag: "#/assets/images/flag_fr_24.png" },
{ id: "br", title: "Português", flag: "#/assets/images/flag_br_24.png" }
]
};
},
computed: {
currentLocaleIndex: function() {
let index = this.locales.findIndex(o => o.id === this.$i18n.locale);
return index;
},
currentLocaleTitle: function() {
let obj = this.locales.find(o => o.id === this.$i18n.locale);
return obj.title;
},
currentLocaleFlag: function() {
let obj = this.locales.find(o => o.id === this.$i18n.locale);
return obj.flag;
}
},
methods: {
flagImage: function(index) {
return require("#/assets/images/flag_" +
this.locales[index].id +
"_24.png");
},
flagTitle: function(index) {
return this.locales[index].title;
},
switchLocale: function(index) {
this.$i18n.locale = this.locales[index].id;
}
},
mounted() {}
};
</script>
Toobar.spec.je
import Vue from "vue";
import router from "#/router";
import Vuetify from "vuetify";
import i18n from "#/locales";
import { mount, shallowMount } from "#vue/test-utils";
import Toolbar from "#/components/shared/Toolbar.vue";
describe("App.vue", () => {
let wrapper;
beforeEach(() => {
Vue.use(Vuetify);
Vue.filter("translate", function(value) {
if (!value) return "";
value = "lang.views.global." + value.toString();
return i18n.t(value);
});
const el = document.createElement('div');
el.setAttribute('data-app', true);
document.body.appendChild(el);
});
it("should change locale", () => {
// given
wrapper = mount(Toolbar, { router, i18n });
console.log('CURRENT LOCALE INDEX: ', wrapper.vm.currentLocaleIndex);
// console.log(wrapper.html());
const currentFlagBtn = wrapper.find("#current-flag");
console.log(currentFlagBtn.html())
currentFlagBtn.trigger('mouseover');
wrapper.vm.$nextTick( () => {
console.log(wrapper.html());
// const localesBtn = wrapper.findAll("btn");
});
// when
// localesBtn.at(1).trigger("click"); // French locale
// then
// expect(wrapper.wrapper.vm.currentLocaleIndex).toBe(1);
});
});
console.log
console.log tests/unit/Toolbar.spec.js:32
CURRENT LOCALE INDEX: 0
console.log tests/unit/Toolbar.spec.js:35
<button type="button" class="v-btn v-btn--flat" id="current-flag"><div class="v-btn__content"><img src="[object Object]" width="24px"></div></button>
console.log tests/unit/Toolbar.spec.js:38
<nav class="v-toolbar v-toolbar--fixed" style="margin-top: 0px; padding-right: 0px; padding-left: 0px; transform: translateY(0px);">
<div class="v-toolbar__content" style="height: 80px;">
<div class="v-toolbar__title">
<img src="#/assets/images/app_logo.png" alt="">
<div class="v-menu btn btn--flat v-menu--inline" style="margin-bottom: 12px;">
<div class="v-menu__activator">
<button type="button" class="v-btn v-btn--flat" id="current-flag">
<div class="v-btn__content">
<img src="[object Object]" width="24px">
</div>
</button>
</div>
</div>
</div>
<div class="spacer"></div>
<div class="v-toolbar__items">
<button type="button" class="v-btn v-btn--flat">
<div class="v-btn__content">
<i aria-hidden="true"class="v-icon v-icon--left material-icons">home</i>
<span>Home</span>
</div>
</button>
<button type="button" class="v-btn v-btn--flat">
<div class="v-btn__content">
<i aria-hidden="true" class="v-icon v-icon--left material-icons">info</i>
<span>About</span>
</div>
</button>
</div>
</div>
</nav>
I found a solution, but maybe someone can explain why :
btnFlags.at(1).vm.$emit('click'); // OK
and
btnFlags.at(1).trigger('click'); // NOT OK
this is the spec which is running fine :
import Vue from "vue";
import router from "#/router";
import Vuetify from "vuetify";
import i18n from "#/locales";
import { mount, shallowMount } from "#vue/test-utils";
import Toolbar from "#/components/shared/Toolbar.vue";
describe("Toolbar.vue", () => {
let wrapper;
beforeEach(() => {
Vue.use(Vuetify);
Vue.filter("translate", function(value) {
if (!value) return "";
value = "lang.views.global." + value.toString();
return i18n.t(value);
});
const el = document.createElement('div');
el.setAttribute('data-app', true);
document.body.appendChild(el);
});
it("should change locale", async () => {
// given
wrapper = shallowMount(Toolbar, { router, i18n });
console.log('CURRENT LOCALE INDEX: ', wrapper.vm.currentLocaleIndex);
const btnFlags = wrapper.findAll("v-list-tile-stub");
// when
btnFlags.at(1).vm.$emit('click');
await wrapper.vm.$nextTick();
// then
console.log('NEW LOCALE INDEX: ', wrapper.vm.currentLocaleIndex);
expect(wrapper.vm.currentLocaleIndex).toBe(1);
});
});

I am try to login with django using Angular4

i create Rest API for login in Django and i want to call it in angular4
heare is my angular4 code
login.components.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Http, Response, Headers} from '#angular/http';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private router:Router, private http:Http) { }
getData = [];
with this function i get data from my APIand i am trying to compare this data with form data
fatchData = function(){
this.http.get("http://127.0.0.1:8000/loginAdmin/").subscribe((res:Response) => {
this.getData = res.json();
})
}
loginUser(e){
e.preventDefault();
var username = e.target.elements[0].value;
var password = e.target.elements[1].value;
if(username == 'this.getData.username' && password == 'this.getData.password')
{
this.router.navigate(['/deshbord'])
}
else{
console.log('Error Find');
}
}
ngOnInit() {
this.fatchData();
}
}
this is my login.comonents.login i want that when user give user name and password its compare with my API data and it's true then navigate to other page..
<form (submit)="loginUser($event)">
<div class="input">
<label>UserName</label>
<input type="text">
</div>
<div class="input">
<label>password</label>
<input type="password">
</div>
<div class="input">
<input type="submit" value="Login">
</div>
</form>